Merge "Verify Gerrit changes using Chrome 80" into stable-3.2
diff --git a/WORKSPACE b/WORKSPACE
index 97fadaf..104e727 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -663,18 +663,18 @@
     sha1 = "a3ae34e57fa8a4040e28247291d0cc3d6b8c7bcf",
 )
 
-AUTO_VALUE_VERSION = "1.7"
+AUTO_VALUE_VERSION = "1.7.1"
 
 maven_jar(
     name = "auto-value",
     artifact = "com.google.auto.value:auto-value:" + AUTO_VALUE_VERSION,
-    sha1 = "fe8387764ed19460eda4f106849c664f51c07121",
+    sha1 = "b995de0a53b58b59c08a3d6f4f9379b29b8e4bdc",
 )
 
 maven_jar(
     name = "auto-value-annotations",
     artifact = "com.google.auto.value:auto-value-annotations:" + AUTO_VALUE_VERSION,
-    sha1 = "5be124948ebdc7807df68207f35a0f23ce427f29",
+    sha1 = "58d76a9ec581f7c6d33f3343de9b2ba04a0ae799",
 )
 
 declare_nongoogle_deps()
@@ -766,7 +766,7 @@
     sha1 = "f7be08ec23c21485b9b5a1cf1654c2ec8c58168d",
 )
 
-GITILES_VERS = "0.3-7"
+GITILES_VERS = "0.4"
 
 GITILES_REPO = GERRIT
 
@@ -775,14 +775,14 @@
     artifact = "com.google.gitiles:blame-cache:" + GITILES_VERS,
     attach_source = False,
     repository = GITILES_REPO,
-    sha1 = "af6212a62363906c63d367f8276ae1645f83bf93",
+    sha1 = "567198123898aa86bd854d3fcb044dc7a1845741",
 )
 
 maven_jar(
     name = "gitiles-servlet",
     artifact = "com.google.gitiles:gitiles-servlet:" + GITILES_VERS,
     repository = GITILES_REPO,
-    sha1 = "6a53f722f8572a2f1bcb7d86e5692168844bab76",
+    sha1 = "0dd832a6df108af0c75ae29b752fda64ccbd6886",
 )
 
 # prettify must match the version used in Gitiles
@@ -912,48 +912,48 @@
     sha1 = "7e060dd5b19431e6d198e91ff670644372f60fbd",
 )
 
-JETTY_VERS = "9.4.24.v20191120"
+JETTY_VERS = "9.4.27.v20200227"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VERS,
-    sha1 = "ca1803fde51b795c0a8346ca8bc6277d9d04d01d",
+    sha1 = "c6354d1e53c41f839ae56f4d8622c866a1ad8487",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VERS,
-    sha1 = "9fa640d36c088cf55843900043d28aef830ade4d",
+    sha1 = "aead56f2a1ac49d720a192cb7c1568e61e34ddae",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VERS,
-    sha1 = "7885cc3d5d7701a444acada7ab97f89846514875",
+    sha1 = "4ef690ce1277e3767d457f87621f2c436a001881",
 )
 
 maven_jar(
     name = "jetty-jmx",
     artifact = "org.eclipse.jetty:jetty-jmx:" + JETTY_VERS,
-    sha1 = "22be18a055850a6cf3b0efd56c789c3929c87e98",
+    sha1 = "df66265ec011d8b33a7fa541774257deb957ecb4",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VERS,
-    sha1 = "d3f0b0fb016ef8d35ffb199d928ffbcbfa121c86",
+    sha1 = "722ba6ef20eb58c55868f1ce85411e6af13be98e",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VERS,
-    sha1 = "dcb6d4d505ef74898e3a64a38c40195c01e97119",
+    sha1 = "e85e7c4f298efb36b80cc53d635f2da776aa54c2",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VERS,
-    sha1 = "3095acb088f4ff9e3fd9aedf98db73e3c18ea849",
+    sha1 = "44087a126227af5196e3e327a5e11aad1b28852c",
 )
 
 maven_jar(
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 0ba4905..44dc6e1 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -19,7 +19,6 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.common.Nullable;
@@ -47,6 +46,7 @@
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.GroupCollector;
 import com.google.gerrit.server.git.MergeUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
@@ -173,7 +173,6 @@
         TimeUtil.nowTs(),
         null,
         null,
-        null,
         null);
   }
 
@@ -205,7 +204,7 @@
       throws IOException, InvalidChangeOperationException, UpdateException, RestApiException,
           ConfigInvalidException, NoSuchProjectException {
     return cherryPick(
-        sourceChange, project, sourceCommit, input, dest, TimeUtil.nowTs(), null, null, null, null);
+        sourceChange, project, sourceCommit, input, dest, TimeUtil.nowTs(), null, null, null);
   }
 
   /**
@@ -227,8 +226,6 @@
    * @param idForNewChange The ID that the new change of the cherry pick will have. If provided and
    *     the cherry-pick doesn't result in creating a new change, then
    *     InvalidChangeOperationException is thrown.
-   * @param groupName The name of the group for grouping related changes (used by GetRelated
-   *     endpoint).
    * @return Result object that describes the cherry pick.
    * @throws IOException Unable to open repository or read from the database.
    * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
@@ -248,8 +245,7 @@
       Timestamp timestamp,
       @Nullable Change.Id revertedChange,
       @Nullable ObjectId changeIdForNewChange,
-      @Nullable Change.Id idForNewChange,
-      @Nullable String groupName)
+      @Nullable Change.Id idForNewChange)
       throws IOException, InvalidChangeOperationException, UpdateException, RestApiException,
           ConfigInvalidException, NoSuchProjectException {
 
@@ -379,12 +375,12 @@
                     cherryPickCommit,
                     dest.branch(),
                     newTopic,
+                    project,
                     sourceChange,
                     sourceCommit,
                     input,
                     revertedChange,
-                    idForNewChange,
-                    groupName);
+                    idForNewChange);
           }
           bu.execute();
           return Result.create(changeId, cherryPickCommit.getFilesWithGitConflicts());
@@ -473,13 +469,13 @@
       CodeReviewCommit cherryPickCommit,
       String refName,
       String topic,
+      Project.NameKey project,
       @Nullable Change sourceChange,
       @Nullable ObjectId sourceCommit,
       CherryPickInput input,
       @Nullable Change.Id revertOf,
-      @Nullable Change.Id idForNewChange,
-      @Nullable String groupName)
-      throws IOException {
+      @Nullable Change.Id idForNewChange)
+      throws IOException, InvalidChangeOperationException {
     Change.Id changeId = idForNewChange != null ? idForNewChange : Change.id(seq.nextChangeId());
     ChangeInserter ins = changeInserterFactory.create(changeId, cherryPickCommit, refName);
     ins.setRevertOf(revertOf);
@@ -506,8 +502,23 @@
       Set<Account.Id> ccs = new HashSet<>(reviewerSet.byState(ReviewerStateInternal.CC));
       ccs.remove(user.get().getAccountId());
       ins.setReviewersAndCcs(reviewers, ccs);
-      if (groupName != null) {
-        ins.setGroups(ImmutableList.of(groupName));
+    }
+    // If there is a base, and the base is not merged, the groups will be overridden by the base's
+    // groups.
+    ins.setGroups(GroupCollector.getDefaultGroups(cherryPickCommit.getId()));
+    if (input.base != null) {
+      List<ChangeData> changes =
+          queryProvider.get().setLimit(2).byBranchCommitOpen(project.get(), refName, input.base);
+      if (changes.size() > 1) {
+        throw new InvalidChangeOperationException(
+            "Several changes with key "
+                + input.base
+                + " reside on the same branch. "
+                + "Cannot cherry-pick on target branch.");
+      }
+      if (changes.size() == 1) {
+        Change change = changes.get(0).change();
+        ins.setGroups(changeNotesFactory.createChecked(change).getCurrentPatchSet().groups());
       }
     }
     bu.insertChange(ins);
diff --git a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
index 7a04f05..846b80c 100644
--- a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
+++ b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
@@ -286,7 +286,6 @@
       throws IOException, RestApiException, UpdateException, ConfigInvalidException,
           PermissionBackendException {
 
-    String groupName = null;
     String initialMessage = revertInput.message;
     while (sortedChangesInProjectAndBranch.hasNext()) {
       ChangeNotes changeNotes = sortedChangesInProjectAndBranch.next().data().notes();
@@ -300,13 +299,8 @@
         // This is the code in case this is the first revert of this project + branch, and the
         // revert would be on top of the change being reverted.
         craeteNormalRevert(revertInput, changeNotes, timestamp);
-        groupName = cherryPickInput.base;
       } else {
-        // This is the code in case this is the second revert (or more) of this project + branch.
-        if (groupName == null) {
-          groupName = cherryPickInput.base;
-        }
-        createCherryPickedRevert(revertInput, project, groupName, changeNotes, timestamp);
+        createCherryPickedRevert(revertInput, project, changeNotes, timestamp);
       }
     }
   }
@@ -314,7 +308,6 @@
   private void createCherryPickedRevert(
       RevertInput revertInput,
       Project.NameKey project,
-      String groupName,
       ChangeNotes changeNotes,
       Timestamp timestamp)
       throws IOException, ConfigInvalidException, UpdateException, RestApiException {
@@ -333,7 +326,7 @@
       bu.addOp(
           changeNotes.getChange().getId(),
           new CreateCherryPickOp(
-              revCommitId, generatedChangeId, cherryPickRevertChangeId, groupName, timestamp));
+              revCommitId, generatedChangeId, cherryPickRevertChangeId, timestamp));
       bu.addOp(changeNotes.getChange().getId(), new PostRevertedMessageOp(generatedChangeId));
       bu.addOp(
           cherryPickRevertChangeId,
@@ -547,19 +540,16 @@
     private final ObjectId revCommitId;
     private final ObjectId computedChangeId;
     private final Change.Id cherryPickRevertChangeId;
-    private final String groupName;
     private final Timestamp timestamp;
 
     CreateCherryPickOp(
         ObjectId revCommitId,
         ObjectId computedChangeId,
         Change.Id cherryPickRevertChangeId,
-        String groupName,
         Timestamp timestamp) {
       this.revCommitId = revCommitId;
       this.computedChangeId = computedChangeId;
       this.cherryPickRevertChangeId = cherryPickRevertChangeId;
-      this.groupName = groupName;
       this.timestamp = timestamp;
     }
 
@@ -577,8 +567,7 @@
               timestamp,
               change.getId(),
               computedChangeId,
-              cherryPickRevertChangeId,
-              groupName);
+              cherryPickRevertChangeId);
       // save the commit as base for next cherryPick of that branch
       cherryPickInput.base =
           changeNotesFactory
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 17391de..74f9134 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -1097,6 +1097,57 @@
   }
 
   @Test
+  public void getRelatedCherryPicks() throws Exception {
+    PushOneCommit.Result r1 = createChange(SUBJECT, "a.txt", "a");
+    PushOneCommit.Result r2 = createChange(SUBJECT, "b.txt", "b");
+
+    String branch = "foo";
+    // Create target branch to cherry-pick to.
+    gApi.projects().name(project.get()).branch(branch).create(new BranchInput());
+
+    CherryPickInput input = new CherryPickInput();
+    input.message = "message";
+    input.destination = branch;
+    ChangeInfo firstCherryPickResult =
+        gApi.changes().id(r1.getChangeId()).current().cherryPickAsInfo(input);
+
+    input.base = gApi.changes().id(firstCherryPickResult.changeId).current().commit(false).commit;
+    ChangeInfo secondCherryPickResult =
+        gApi.changes().id(r2.getChangeId()).current().cherryPickAsInfo(input);
+    assertThat(gApi.changes().id(firstCherryPickResult.changeId).current().related().changes)
+        .hasSize(2);
+    assertThat(gApi.changes().id(secondCherryPickResult.changeId).current().related().changes)
+        .hasSize(2);
+  }
+
+  @Test
+  public void cherryPickOnMergedChangeIsNotRelated() throws Exception {
+    PushOneCommit.Result r1 = createChange(SUBJECT, "a.txt", "a");
+    PushOneCommit.Result r2 = createChange(SUBJECT, "b.txt", "b");
+
+    String branch = "foo";
+    // Create target branch to cherry-pick to.
+    gApi.projects().name(project.get()).branch(branch).create(new BranchInput());
+
+    CherryPickInput input = new CherryPickInput();
+    input.message = "message";
+    input.destination = branch;
+    ChangeInfo firstCherryPickResult =
+        gApi.changes().id(r1.getChangeId()).current().cherryPickAsInfo(input);
+
+    gApi.changes().id(firstCherryPickResult.id).current().review(ReviewInput.approve());
+    gApi.changes().id(firstCherryPickResult.id).current().submit();
+
+    input.base = gApi.changes().id(firstCherryPickResult.changeId).current().commit(false).commit;
+    ChangeInfo secondCherryPickResult =
+        gApi.changes().id(r2.getChangeId()).current().cherryPickAsInfo(input);
+    assertThat(gApi.changes().id(firstCherryPickResult.changeId).current().related().changes)
+        .hasSize(0);
+    assertThat(gApi.changes().id(secondCherryPickResult.changeId).current().related().changes)
+        .hasSize(0);
+  }
+
+  @Test
   public void canRebase() throws Exception {
     PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r1 = push.to("refs/for/master");
diff --git a/plugins/plugin-manager b/plugins/plugin-manager
index 2933add..93d8e12 160000
--- a/plugins/plugin-manager
+++ b/plugins/plugin-manager
@@ -1 +1 @@
-Subproject commit 2933add62ecf2cbfc28cfe2cff81ff0e0eecc913
+Subproject commit 93d8e1248478c6b2db0a8c1080f2766406dd213d
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index 101f304..906ed34 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -241,11 +241,6 @@
     return mappedLines.join('\n').trim();
   }
 
-  _isMessageContentEmpty() {
-    return !this._messageContentExpanded
-        || this._messageContentExpanded.length === 0;
-  }
-
   _computeAuthor(message) {
     return message.author || message.updated_by;
   }
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
index c8e1cc1..753fd38 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
@@ -217,7 +217,7 @@
             content="[[_messageContentExpanded]]"
             config="[[_projectConfig.commentlinks]]"
           ></gr-formatted-text>
-          <template is="dom-if" if="[[!_isMessageContentEmpty()]]">
+          <template is="dom-if" if="[[_messageContentExpanded]]">
             <div
               class="replyActionContainer"
               hidden$="[[!showReplyButton]]"
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index 5e2feb4..4651e66 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -425,6 +425,32 @@
           element.shadowRoot.querySelector('.deleteBtn').hidden
       );
     });
+
+    test('reply button shown when message is updated', () => {
+      element.message = undefined;
+      flushAsynchronousOperations();
+      let replyEl = element.shadowRoot.querySelector('.replyActionContainer');
+      // We don't even expect the button to show up in the DOM when the message
+      // is undefined.
+      assert.isNotOk(replyEl);
+
+      element.message = {
+        id: '47c43261_55aa2c41',
+        author: {
+          _account_id: 1115495,
+          name: 'Andrew Bonventre',
+          email: 'andybons@chromium.org',
+        },
+        date: '2016-01-12 20:24:49.448000000',
+        message: 'not empty',
+        _revision_number: 1,
+        expanded: false,
+      };
+      flushAsynchronousOperations();
+      replyEl = element.shadowRoot.querySelector('.replyActionContainer');
+      assert.isOk(replyEl);
+      assert.isFalse(replyEl.hidden);
+    });
   });
 });
 </script>