Merge "MergeSuperSet: Handle changes that exist in index but not in NoteDb gracefully"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index f99973a..dcd8121 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1515,6 +1515,8 @@
 
 Creates open revert changes for all of the changes of a certain submission.
 
+The subject of each revert change will be "Revert <subject-of-reverted-change".
+
 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}`.
@@ -3902,6 +3904,10 @@
 The review must be provided in the request body as a
 link:#review-input[ReviewInput] entity.
 
+If the labels are set, the user sending the request will automatically be
+added as a reviewer, otherwise (if they only commented) they are added to
+the CC list.
+
 A review cannot be set on a change edit. Trying to post a review for a
 change edit fails with `409 Conflict`.
 
diff --git a/java/com/google/gerrit/server/patch/AutoMerger.java b/java/com/google/gerrit/server/patch/AutoMerger.java
index a9cc9b5..2e0214c 100644
--- a/java/com/google/gerrit/server/patch/AutoMerger.java
+++ b/java/com/google/gerrit/server/patch/AutoMerger.java
@@ -16,14 +16,17 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 
-import com.google.common.flogger.FluentLogger;
+import com.google.common.base.Throwables;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.git.LockFailureException;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.InMemoryInserter;
 import com.google.gerrit.server.git.MergeUtil;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryableAction.ActionType;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.dircache.DirCache;
@@ -42,28 +45,54 @@
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
 
+/**
+ * Utility class for creating an auto-merge commit of a merge commit.
+ *
+ * <p>An auto-merge commit is the result of merging the 2 parents of a merge commit automatically.
+ * If there are conflicts the auto-merge commit contains Git conflict markers that indicate these
+ * conflicts.
+ *
+ * <p>Creating auto-merge commits for octopus merges (merge commits with more than 2 parents) is not
+ * supported. In this case the auto-merge is created between the first 2 parent commits.
+ *
+ * <p>All created auto-merge commits are stored in the repository of their merge commit as {@code
+ * refs/cache-automerge/} branches. These branches serve:
+ *
+ * <ul>
+ *   <li>as a cache so that the each auto-merge gets computed only once
+ *   <li>as base for merge commits on which users can comment
+ * </ul>
+ *
+ * <p>The second point means that these commits are referenced from NoteDb. The consequence of this
+ * is that these refs should never be deleted.
+ */
 public class AutoMerger {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
   @UsedAt(UsedAt.Project.GOOGLE)
   public static boolean cacheAutomerge(Config cfg) {
     return cfg.getBoolean("change", null, "cacheAutomerge", true);
   }
 
+  private final RetryHelper retryHelper;
   private final PersonIdent gerritIdent;
   private final boolean save;
 
   @Inject
-  AutoMerger(@GerritServerConfig Config cfg, @GerritPersonIdent PersonIdent gerritIdent) {
+  AutoMerger(
+      RetryHelper retryHelper,
+      @GerritServerConfig Config cfg,
+      @GerritPersonIdent PersonIdent gerritIdent) {
+    this.retryHelper = retryHelper;
     save = cacheAutomerge(cfg);
     this.gerritIdent = gerritIdent;
   }
 
   /**
-   * Perform an auto-merge of the parents of the given merge commit.
+   * Creates an auto-merge commit of the parents of the given merge commit.
    *
-   * @return auto-merge commit or {@code null} if an auto-merge commit couldn't be created. Headers
-   *     of the returned RevCommit are parsed.
+   * <p>In case of an exception the creation of the auto-merge commit is retried a few times. E.g.
+   * this allows the operation to succeed if a Git update fails due to a temporary issue.
+   *
+   * @return auto-merge commit. Headers of the returned RevCommit are parsed.
    */
   public RevCommit merge(
       Repository repo,
@@ -72,7 +101,34 @@
       RevCommit merge,
       ThreeWayMergeStrategy mergeStrategy)
       throws IOException {
+    try {
+      return retryHelper
+          .action(
+              ActionType.GIT_UPDATE,
+              "createAutoMerge",
+              () -> createAutoMergeCommit(repo, rw, ins, merge, mergeStrategy))
+          .call();
+    } catch (Exception e) {
+      Throwables.throwIfUnchecked(e);
+      Throwables.throwIfInstanceOf(e, IOException.class);
+      throw new IllegalStateException(e);
+    }
+  }
+
+  /**
+   * Creates an auto-merge commit of the parents of the given merge commit.
+   *
+   * @return auto-merge commit. Headers of the returned RevCommit are parsed.
+   */
+  private RevCommit createAutoMergeCommit(
+      Repository repo,
+      RevWalk rw,
+      ObjectInserter ins,
+      RevCommit merge,
+      ThreeWayMergeStrategy mergeStrategy)
+      throws IOException {
     checkArgument(rw.getObjectReader().getCreatedFromInserter() == ins);
+
     InMemoryInserter tmpIns = null;
     if (ins instanceof InMemoryInserter) {
       // Caller gave us an in-memory inserter, so ensure anything we write from
@@ -102,17 +158,7 @@
     m.setDirCache(dc);
     m.setObjectInserter(tmpIns == null ? new NonFlushingWrapper(ins) : tmpIns);
 
-    boolean couldMerge;
-    try {
-      couldMerge = m.merge(merge.getParents());
-    } catch (IOException | RuntimeException e) {
-      // It is not safe to continue further down in this method as throwing
-      // an exception most likely means that the merge tree was not created
-      // and m.getMergeResults() is empty. This would mean that all paths are
-      // unmerged and Gerrit UI would show all paths in the patch list.
-      logger.atWarning().withCause(e).log("Error attempting automerge %s", refName);
-      return null;
-    }
+    boolean couldMerge = m.merge(merge.getParents());
 
     ObjectId treeId;
     if (couldMerge) {
@@ -173,8 +219,28 @@
     RefUpdate ru = repo.updateRef(refName);
     ru.setNewObjectId(commitId);
     ru.disableRefLog();
-    ru.forceUpdate();
-    return rw.parseCommit(commitId);
+    switch (ru.forceUpdate()) {
+      case FAST_FORWARD:
+      case FORCED:
+      case NEW:
+      case NO_CHANGE:
+        return rw.parseCommit(commitId);
+      case LOCK_FAILURE:
+        throw new LockFailureException(
+            String.format("Failed to create auto-merge of %s", merge.name()), ru);
+      case IO_FAILURE:
+      case NOT_ATTEMPTED:
+      case REJECTED:
+      case REJECTED_CURRENT_BRANCH:
+      case REJECTED_MISSING_OBJECT:
+      case REJECTED_OTHER_REASON:
+      case RENAMED:
+      default:
+        throw new IOException(
+            String.format(
+                "Failed to create auto-merge of %s: Cannot write %s (%s)",
+                merge.name(), refName, ru.getResult()));
+    }
   }
 
   private static class NonFlushingWrapper extends ObjectInserter.Filter {
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index df5d291..5f076b1 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -25,6 +25,7 @@
 import com.google.common.base.Enums;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
+import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.GroupReference;
@@ -100,6 +101,8 @@
 
 /** Parses a query string meant to be applied to change objects. */
 public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuilder> {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
   public interface ChangeOperatorFactory extends OperatorFactory<ChangeData, ChangeQueryBuilder> {}
 
   /**
@@ -1441,8 +1444,14 @@
                 accounts.stream()
                     .map(id -> ReviewerPredicate.forState(id, state))
                     .collect(toList()));
+      } else {
+        logger.atFine().log(
+            "Skipping reviewer predicate for %s in default field query"
+                + " because the number of matched accounts (%d) exceeds the limit of %d",
+            who, accounts.size(), MAX_ACCOUNTS_PER_DEFAULT_FIELD);
       }
     } catch (QueryParseException e) {
+      logger.atFine().log("Parsing %s as account failed: %s", who, e.getMessage());
       // Propagate this exception only if we can't use 'who' to query by email
       if (reviewerByEmailPredicate == null) {
         throw e;
diff --git a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
index be33405..df4c83e 100644
--- a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
+++ b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
@@ -250,6 +250,8 @@
         if (cherryPickInput.base == null) {
           cherryPickInput.base = getBase(changeNotes, commitIdsInProjectAndBranch).name();
         }
+
+        revertInput.message = getMessage(revertInput, changeNotes);
         // This is the code in case this is the first revert of this project + branch, and the
         // revert would be on top of the change being reverted.
         if (cherryPickInput.base.equals(changeNotes.getCurrentPatchSet().commitId().getName())) {
@@ -271,13 +273,7 @@
               commitUtil.createRevertCommit(revertInput.message, changeNotes, user.get());
           // TODO (paiking): As a future change, the revert should just be done directly on the
           // target rather than just creating a commit and then cherry-picking it.
-          cherryPickInput.message =
-              revertInput.message != null
-                  ? revertInput.message
-                  : MessageFormat.format(
-                      ChangeMessages.get().revertChangeDefaultMessage,
-                      changeNotes.getChange().getSubject(),
-                      changeNotes.getCurrentPatchSet().commitId().name());
+          cherryPickInput.message = revertInput.message;
           ObjectId generatedChangeId = Change.generateChangeId();
           Change.Id cherryPickRevertChangeId = Change.id(seq.nextChangeId());
           if (groupName == null) {
@@ -315,6 +311,18 @@
     return revertSubmissionInfo;
   }
 
+  private String getMessage(RevertInput revertInput, ChangeNotes changeNotes) {
+    String subject = changeNotes.getChange().getSubject();
+    if (revertInput.message == null) {
+      return MessageFormat.format(
+          ChangeMessages.get().revertChangeDefaultMessage,
+          subject,
+          changeNotes.getCurrentPatchSet().commitId().name());
+    }
+
+    return String.format("Revert \"%s\"\n\n%s", subject, revertInput.message);
+  }
+
   /**
    * This function finds the base that the first revert in a project + branch should be based on. It
    * searches using BFS for the first commit that is either: 1. Has 2 or more parents, and has as
diff --git a/java/com/google/gerrit/server/update/RetryableAction.java b/java/com/google/gerrit/server/update/RetryableAction.java
index 75ebeb37..167b209 100644
--- a/java/com/google/gerrit/server/update/RetryableAction.java
+++ b/java/com/google/gerrit/server/update/RetryableAction.java
@@ -51,6 +51,7 @@
   public enum ActionType {
     ACCOUNT_UPDATE,
     CHANGE_UPDATE,
+    GIT_UPDATE,
     GROUP_UPDATE,
     INDEX_QUERY,
     PLUGIN_UPDATE,
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
index 61dd31a..f1ea319 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
@@ -672,13 +672,36 @@
 
   @Test
   public void revertSubmissionWithSetMessage() throws Exception {
-    String result = createChange().getChangeId();
+    String result = createChange("change", "a.txt", "message").getChangeId();
     gApi.changes().id(result).current().review(ReviewInput.approve());
     gApi.changes().id(result).current().submit();
     RevertInput revertInput = new RevertInput();
-    revertInput.message = "Message from input";
-    assertThat(gApi.changes().id(result).revertSubmission(revertInput).revertChanges.get(0).subject)
-        .isEqualTo(revertInput.message);
+    String commitMessage = "Message from input";
+    revertInput.message = commitMessage;
+    ChangeInfo revertChange =
+        gApi.changes().id(result).revertSubmission(revertInput).revertChanges.get(0);
+    assertThat(revertChange.subject).isEqualTo("Revert \"change\"");
+    assertThat(gApi.changes().id(revertChange.id).current().commit(false).message)
+        .isEqualTo(
+            String.format(
+                "Revert \"change\"\n\n%s\n\nChange-Id: %s\n",
+                commitMessage, revertChange.changeId));
+  }
+
+  @Test
+  public void revertSubmissionWithoutMessage() throws Exception {
+    String result = createChange("change", "a.txt", "message").getChangeId();
+    gApi.changes().id(result).current().review(ReviewInput.approve());
+    gApi.changes().id(result).current().submit();
+    RevertInput revertInput = new RevertInput();
+    ChangeInfo revertChange =
+        gApi.changes().id(result).revertSubmission(revertInput).revertChanges.get(0);
+    assertThat(revertChange.subject).isEqualTo("Revert \"change\"");
+    assertThat(gApi.changes().id(revertChange.id).current().commit(false).message)
+        .isEqualTo(
+            String.format(
+                "Revert \"change\"\n\nThis reverts commit %s.\n\nChange-Id: %s\n",
+                gApi.changes().id(result).get().currentRevision, revertChange.changeId));
   }
 
   @Test
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index e836ccc..c177283 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -269,16 +269,6 @@
       </section>
       <section class="actions">
         <div class="left">
-          <template is="dom-if" if="[[canBeStarted]]">
-            <gr-button
-                link
-                secondary
-                disabled="[[_isState(knownLatestState, 'not-latest')]]"
-                class="action save"
-                has-tooltip
-                title="[[_saveTooltip]]"
-                on-click="_saveTapHandler">Save</gr-button>
-          </template>
           <span
               id="checkingStatusLabel"
               hidden$="[[!_isState(knownLatestState, 'checking')]]">
@@ -297,6 +287,18 @@
               id="cancelButton"
               class="action cancel"
               on-click="_cancelTapHandler">Cancel</gr-button>
+          <template is="dom-if" if="[[canBeStarted]]">
+            <!-- Use 'Send' here as the change may only about reviewers / ccs
+              and when this button is visible, the next button will always
+              be 'Start review' -->
+            <gr-button
+                link
+                disabled="[[_isState(knownLatestState, 'not-latest')]]"
+                class="action save"
+                has-tooltip
+                title="[[_saveTooltip]]"
+                on-click="_saveClickHandler">Send</gr-button>
+          </template>
           <gr-button
               id="sendButton"
               link
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index 9551a46..3ce4f1a 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -43,7 +43,7 @@
   };
 
   const ButtonTooltips = {
-    SAVE: 'Save reply but do not send notification',
+    SAVE: 'Send but do not send notification or change review state',
     START_REVIEW: 'Mark as ready for review and send reply',
     SEND: 'Send reply',
   };
@@ -686,7 +686,7 @@
       this._rebuildReviewerArrays(this.change.reviewers, this._owner);
     }
 
-    _saveTapHandler(e) {
+    _saveClickHandler(e) {
       e.preventDefault();
       if (!this.$.ccs.submitEntryText()) {
         // Do not proceed with the save if there is an invalid email entry in
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index 87caf64..fd75c7c 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -116,13 +116,6 @@
       :host([link][primary]) {
         --text-color: var(--primary-button-background-color);
       }
-      :host([secondary]) {
-        --background-color: var(--secondary-button-text-color);
-        --text-color: var(--secondary-button-background-color);
-      }
-      :host([link][secondary]) {
-        --text-color: var(--secondary-button-text-color);
-      }
 
       /* Keep below color definition for primary so that this takes precedence
         when disabled. */
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
index 0567777..d0a07b6 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
@@ -110,25 +110,21 @@
           <gr-button
               id="replyBtn"
               link
-              secondary
               class="action reply"
               on-click="_handleCommentReply">Reply</gr-button>
           <gr-button
               id="quoteBtn"
               link
-              secondary
               class="action quote"
               on-click="_handleCommentQuote">Quote</gr-button>
           <gr-button
               id="ackBtn"
               link
-              secondary
               class="action ack"
               on-click="_handleCommentAck">Ack</gr-button>
           <gr-button
               id="doneBtn"
               link
-              secondary
               class="action done"
               on-click="_handleCommentDone">Done</gr-button>
         </div>
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
index ea68e5f..a96cf44 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
@@ -119,7 +119,6 @@
         --gr-button: {
           height: 20px;
           padding: 0 var(--spacing-s);
-          color: var(--default-button-text-color);
         }
       }
       .editMessage {
@@ -268,7 +267,6 @@
         <gr-button
             id="deleteBtn"
             link
-            secondary
             class$="action delete [[_computeDeleteButtonClass(_isAdmin, draft)]]"
             on-click="_handleCommentDelete">
           <iron-icon id="icon" icon="gr-icons:delete"></iron-icon>
@@ -325,22 +323,18 @@
           <div class="rightActions">
             <gr-button
                 link
-                secondary
                 class="action cancel hideOnPublished"
                 on-click="_handleCancel">Cancel</gr-button>
             <gr-button
                 link
-                secondary
                 class="action discard hideOnPublished"
                 on-click="_handleDiscard">Discard</gr-button>
             <gr-button
                 link
-                secondary
                 class="action edit hideOnPublished"
                 on-click="_handleEdit">Edit</gr-button>
             <gr-button
                 link
-                secondary
                 disabled$="[[_computeSaveDisabled(_messageText, comment, resolved)]]"
                 class="action save hideOnPublished"
                 on-click="_handleSave">Save</gr-button>
@@ -351,7 +345,6 @@
             <template is="dom-if" if="[[!_hasHumanReply]]">
               <gr-button
                   link
-                  secondary
                   class="action fix"
                   on-click="_handleFix"
                   disabled="[[robotButtonDisabled]]">
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
index 25f406e..5d2a1ca 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
@@ -60,6 +60,7 @@
     /**
      * Because either the source text or the linkification config has changed,
      * the content should be re-parsed.
+     *
      * @param {string|null|undefined} content The raw, un-linkified source
      *     string to parse.
      * @param {Object|null|undefined} config The server config specifying
@@ -90,6 +91,7 @@
      *   element should be created and attached to the resulting DOM.
      * - To attach an arbitrary fragment: when called with only the `fragment`
      *   argument, the fragment should be attached to the resulting DOM as is.
+     *
      * @param {string|null} text
      * @param {string|null} href
      * @param  {DocumentFragment|undefined} fragment
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
index 61e28cc..1aa707f 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
@@ -19,6 +19,7 @@
 
   /**
    * Pattern describing URLs with supported protocols.
+   *
    * @type {RegExp}
    */
   const URL_PROTOCOL_PATTERN = /^(https?:\/\/|mailto:)/;
@@ -27,6 +28,7 @@
    * Construct a parser for linkifying text. Will linkify plain URLs that appear
    * in the text as well as custom links if any are specified in the linkConfig
    * parameter.
+   *
    * @param {Object|null|undefined} linkConfig Comment links as specified by the
    *     commentlinks field on a project config.
    * @param {Function} callback The callback to be fired when an intermediate
@@ -45,6 +47,7 @@
 
   /**
    * Emit a callback to create a link element.
+   *
    * @param {string} text The text of the link.
    * @param {string} href The URL to use as the href of the link.
    */
@@ -56,6 +59,7 @@
   /**
    * Given the source text and a list of CommentLinkItem objects that were
    * generated by the commentlinks config, emit parsing callbacks.
+   *
    * @param {string} text The chuml of source text over which the outputArray
    *     items range.
    * @param {!Array<Gerrit.CommentLinkItem>} outputArray The list of items to add
@@ -93,6 +97,7 @@
   /**
    * Sort the given array of CommentLinkItems such that the positions are in
    * reverse order.
+   *
    * @param {!Array<Gerrit.CommentLinkItem>} outputArray
    */
   GrLinkTextParser.prototype.sortArrayReverse = function(outputArray) {
@@ -108,6 +113,7 @@
    * - With the `html` paremeter provided, and the `text` and `href` parameters
    *   passed as `null`. In this case, the string of HTML will be parsed and the
    *   first resulting node will be used as the resulting content.
+   *
    * @param {string|null} text The text to use if creating a link.
    * @param {string|null} href The href to use as the URL if creating a link.
    * @param {string|null} html The html to parse and use as the result.
@@ -150,6 +156,7 @@
   /**
    * Create a CommentLinkItem for a link and append it to the given output
    * array.
+   *
    * @param {string|null} text The text for the link.
    * @param {string|null} href The href to use as the URL of the link.
    * @param {number} position The position inside the source text where the link
@@ -172,6 +179,7 @@
   /**
    * Create a CommentLinkItem specified by an HTMl string and append it to the
    * given output array.
+   *
    * @param {string|null} html The html to parse and use as the result.
    * @param {number} position The position inside the source text where the item
    *     starts.
@@ -192,6 +200,7 @@
 
   /**
    * Does the given range overlap with anything already in the item list.
+   *
    * @param {number} position
    * @param {number} length
    * @param {!Array<Gerrit.CommentLinkItem>} outputArray
@@ -214,6 +223,7 @@
   /**
    * Parse the given source text and emit callbacks for the items that are
    * parsed.
+   *
    * @param {string} text
    */
   GrLinkTextParser.prototype.parse = function(text) {
@@ -231,6 +241,7 @@
    *   ba-linkify has found a plain URL and wants it linkified.
    * - With only a `text` parameter provided: this represents the non-link
    *   content that lies between the links the library has found.
+   *
    * @param {string} text
    * @param {string|null|undefined} href
    */
@@ -257,6 +268,7 @@
   /**
    * Walk over the given source text to find matches for comemntlink patterns
    * and emit parse result callbacks.
+   *
    * @param {string} text The raw source text.
    * @param {Object|null|undefined} patterns A comment links specification
    *   object.
diff --git a/polygerrit-ui/app/lint_test.sh b/polygerrit-ui/app/lint_test.sh
index df37e54..4e7d8c9 100755
--- a/polygerrit-ui/app/lint_test.sh
+++ b/polygerrit-ui/app/lint_test.sh
@@ -37,4 +37,4 @@
 # eslint installation.
 npm link eslint eslint-config-google eslint-plugin-html eslint-plugin-jsdoc
 
-${eslint_bin} -c ${UI_PATH}/.eslintrc.json --ignore-pattern 'node_modules/' --ignore-pattern 'bower_components/' --ignore-pattern 'gr-linked-text' --ignore-pattern 'scripts/vendor' --ext .html,.js ${UI_PATH}
+${eslint_bin} -c ${UI_PATH}/.eslintrc.json --ignore-pattern 'node_modules/' --ignore-pattern 'bower_components/' --ignore-pattern 'scripts/vendor' --ext .html,.js ${UI_PATH}
diff --git a/polygerrit-ui/app/styles/themes/app-theme.html b/polygerrit-ui/app/styles/themes/app-theme.html
index 87230e6..53827b5 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.html
+++ b/polygerrit-ui/app/styles/themes/app-theme.html
@@ -36,7 +36,6 @@
   --primary-button-text-color: white;
     /* Used on text color for change list that doesn't need user's attention. */
   --reviewed-text-color: black;
-  --secondary-button-text-color: #212121;
   --tooltip-text-color: white;
   --vote-text-color-recommended: #388e3c;
   --vote-text-color-disliked: #d32f2f;
@@ -52,7 +51,6 @@
   --dialog-background-color: var(--background-color-primary);
   --dropdown-background-color: var(--background-color-primary);
   --expanded-background-color: var(--background-color-tertiary);
-  --secondary-button-background-color: var(--background-color-primary);
   --select-background-color: var(--background-color-secondary);
   --shell-command-background-color: var(--background-color-secondary);
   --shell-command-decoration-background-color: var(--background-color-tertiary);
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index 78bc48b..cfc1f62 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -36,7 +36,6 @@
       --primary-button-text-color: var(--primary-text-color);
         /* Used on text color for change list doesn't need user's attention. */
       --reviewed-text-color: #dadce0;
-      --secondary-button-text-color: var(--deemphasized-text-color);
       --tooltip-text-color: white;
       --vote-text-color-recommended: #388e3c;
       --vote-text-color-disliked: #d32f2f;
@@ -51,12 +50,11 @@
       /* unique background colors */
       --assignee-highlight-color: #3a361c;
       --comment-background-color: #0b162b;
-      --robot-comment-background-color: #e8f0fe;
+      --robot-comment-background-color: rgba(232, 234, 237, 0.08);
       --edit-mode-background-color: #5c0a36;
       --emphasis-color: #383f4a;
       --hover-background-color: rgba(161, 194, 250, 0.2);
       --primary-button-background-color: var(--link-color);
-      --secondary-button-background-color: var(--primary-text-color);
       --selection-background-color: rgba(161, 194, 250, 0.1);
       --tooltip-background-color: #111;
       --unresolved-comment-background-color: #385a9a;