Merge "Replace GerritNav.navigateToChange with fireReload"
diff --git a/java/com/google/gerrit/server/approval/ApprovalInference.java b/java/com/google/gerrit/server/approval/ApprovalInference.java
index 4cb080a..695997a 100644
--- a/java/com/google/gerrit/server/approval/ApprovalInference.java
+++ b/java/com/google/gerrit/server/approval/ApprovalInference.java
@@ -54,6 +54,7 @@
 import java.util.Map;
 import java.util.Optional;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.revwalk.RevWalk;
 
 /**
@@ -134,8 +135,9 @@
       PatchSet.Id psId,
       ChangeKind kind,
       LabelType type,
-      @Nullable Map<String, FileDiffOutput> modifiedFiles,
-      @Nullable Map<String, FileDiffOutput> modifiedFilesLastPatchset) {
+      @Nullable Map<String, FileDiffOutput> baseVsCurrentDiff,
+      @Nullable Map<String, FileDiffOutput> baseVsPriorDiff,
+      @Nullable Map<String, FileDiffOutput> priorVsCurrentDiff) {
     int n = psa.key().patchSetId().get();
     checkArgument(n != psId.get());
 
@@ -185,7 +187,8 @@
           project.getName());
       return true;
     } else if (type.isCopyAllScoresIfListOfFilesDidNotChange()
-        && listOfFilesUnchangedPredicate.match(modifiedFiles, modifiedFilesLastPatchset)) {
+        && listOfFilesUnchangedPredicate.match(
+            baseVsCurrentDiff, baseVsPriorDiff, priorVsCurrentDiff)) {
       logger.atFine().log(
           "approval %d on label %s of patch set %d of change %d can be copied"
               + " to patch set %d because the label has set "
@@ -402,8 +405,9 @@
         priorPatchSet.getValue().id().changeId(),
         changeKind);
 
-    Map<String, FileDiffOutput> modifiedFiles = null;
-    Map<String, FileDiffOutput> modifiedFilesLastPatchSet = null;
+    Map<String, FileDiffOutput> baseVsCurrent = null;
+    Map<String, FileDiffOutput> baseVsPrior = null;
+    Map<String, FileDiffOutput> priorVsCurrent = null;
     LabelTypes labelTypes = project.getLabelTypes();
     for (PatchSetApproval psa : priorApprovals) {
       if (resultByUser.contains(psa.label(), psa.accountId())) {
@@ -411,11 +415,13 @@
       }
       Optional<LabelType> type = labelTypes.byLabel(psa.labelId());
       // Only compute modified files if there is a relevant label, since this is expensive.
-      if (modifiedFiles == null
+      if (baseVsCurrent == null
           && type.isPresent()
           && type.get().isCopyAllScoresIfListOfFilesDidNotChange()) {
-        modifiedFiles = listModifiedFiles(project, patchSet);
-        modifiedFilesLastPatchSet = listModifiedFiles(project, priorPatchSet.getValue());
+        baseVsCurrent = listModifiedFiles(project, patchSet);
+        baseVsPrior = listModifiedFiles(project, priorPatchSet.getValue());
+        priorVsCurrent =
+            listModifiedFiles(project, priorPatchSet.getValue().commitId(), patchSet.commitId());
       }
       if (!type.isPresent()) {
         logger.atFine().log(
@@ -435,8 +441,9 @@
               patchSet.id(),
               changeKind,
               type.get(),
-              modifiedFiles,
-              modifiedFilesLastPatchSet)
+              baseVsCurrent,
+              baseVsPrior,
+              priorVsCurrent)
           && !canCopyBasedOnCopyCondition(notes, psa, patchSet, type.get(), changeKind)) {
         continue;
       }
@@ -465,4 +472,21 @@
           ex);
     }
   }
+
+  /**
+   * Gets the modified files between two commits corresponding to different patchsets of the same
+   * change.
+   */
+  private Map<String, FileDiffOutput> listModifiedFiles(
+      ProjectState project, ObjectId sourceCommit, ObjectId targetCommit) {
+    try {
+      return diffOperations.listModifiedFiles(project.getNameKey(), sourceCommit, targetCommit);
+    } catch (DiffNotAvailableException ex) {
+      throw new StorageException(
+          "failed to compute difference in files, so won't copy"
+              + " votes on labels even if list of files is the same and "
+              + "copyAllIfListOfFilesDidNotChange",
+          ex);
+    }
+  }
 }
diff --git a/java/com/google/gerrit/server/notedb/CommitRewriter.java b/java/com/google/gerrit/server/notedb/CommitRewriter.java
index 3cbe546..e940b1e 100644
--- a/java/com/google/gerrit/server/notedb/CommitRewriter.java
+++ b/java/com/google/gerrit/server/notedb/CommitRewriter.java
@@ -543,12 +543,16 @@
     Matcher assigneeDeletedMatcher = ASSIGNEE_DELETED_PATTERN.matcher(originalChangeMessage);
     if (assigneeDeletedMatcher.matches()) {
       if (!NON_REPLACE_ACCOUNT_PATTERN.matcher(assigneeDeletedMatcher.group(1)).matches()) {
+        Optional<String> assigneeReplacement =
+            getPossibleAccountReplacement(
+                changeFixProgress,
+                oldAssignee,
+                getAccountInfoFromNameEmail(assigneeDeletedMatcher.group(1)));
+
         return Optional.of(
-            "Assignee deleted: "
-                + getPossibleAccountReplacement(
-                    changeFixProgress,
-                    oldAssignee,
-                    ParsedAccountInfo.create(assigneeDeletedMatcher.group(1))));
+            assigneeReplacement.isPresent()
+                ? "Assignee deleted: " + assigneeReplacement.get()
+                : "Assignee was deleted.");
       }
       return Optional.empty();
     }
@@ -556,12 +560,15 @@
     Matcher assigneeAddedMatcher = ASSIGNEE_ADDED_PATTERN.matcher(originalChangeMessage);
     if (assigneeAddedMatcher.matches()) {
       if (!NON_REPLACE_ACCOUNT_PATTERN.matcher(assigneeAddedMatcher.group(1)).matches()) {
+        Optional<String> assigneeReplacement =
+            getPossibleAccountReplacement(
+                changeFixProgress,
+                newAssignee,
+                getAccountInfoFromNameEmail(assigneeAddedMatcher.group(1)));
         return Optional.of(
-            "Assignee added: "
-                + getPossibleAccountReplacement(
-                    changeFixProgress,
-                    newAssignee,
-                    ParsedAccountInfo.create(assigneeAddedMatcher.group(1))));
+            assigneeReplacement.isPresent()
+                ? "Assignee added: " + assigneeReplacement.get()
+                : "Assignee was added.");
       }
       return Optional.empty();
     }
@@ -569,17 +576,22 @@
     Matcher assigneeChangedMatcher = ASSIGNEE_CHANGED_PATTERN.matcher(originalChangeMessage);
     if (assigneeChangedMatcher.matches()) {
       if (!NON_REPLACE_ACCOUNT_PATTERN.matcher(assigneeChangedMatcher.group(1)).matches()) {
+        Optional<String> oldAssigneeReplacement =
+            getPossibleAccountReplacement(
+                changeFixProgress,
+                oldAssignee,
+                getAccountInfoFromNameEmail(assigneeChangedMatcher.group(1)));
+        Optional<String> newAssigneeReplacement =
+            getPossibleAccountReplacement(
+                changeFixProgress,
+                newAssignee,
+                getAccountInfoFromNameEmail(assigneeChangedMatcher.group(2)));
         return Optional.of(
-            String.format(
-                "Assignee changed from: %s to: %s",
-                getPossibleAccountReplacement(
-                    changeFixProgress,
-                    oldAssignee,
-                    ParsedAccountInfo.create(assigneeChangedMatcher.group(1))),
-                getPossibleAccountReplacement(
-                    changeFixProgress,
-                    newAssignee,
-                    ParsedAccountInfo.create(assigneeChangedMatcher.group(2)))));
+            oldAssigneeReplacement.isPresent() && newAssigneeReplacement.isPresent()
+                ? String.format(
+                    "Assignee changed from: %s to: %s",
+                    oldAssigneeReplacement.get(), newAssigneeReplacement.get())
+                : "Assignee was changed.");
       }
       return Optional.empty();
     }
@@ -610,12 +622,15 @@
 
     Matcher matcher = REMOVED_VOTE_PATTERN.matcher(originalChangeMessage);
     if (matcher.matches() && !NON_REPLACE_ACCOUNT_PATTERN.matcher(matcher.group(2)).matches()) {
-      return Optional.of(
-          String.format(
-              "Removed %s by %s",
-              matcher.group(1),
-              getPossibleAccountReplacement(
-                  changeFixProgress, reviewer, getAccountInfoFromNameEmail(matcher.group(2)))));
+      Optional<String> reviewerReplacement =
+          getPossibleAccountReplacement(
+              changeFixProgress, reviewer, getAccountInfoFromNameEmail(matcher.group(2)));
+      StringBuilder replacement = new StringBuilder();
+      replacement.append("Removed ").append(matcher.group(1));
+      if (reviewerReplacement.isPresent()) {
+        replacement.append(" by ").append(reviewerReplacement.get());
+      }
+      return Optional.of(replacement.toString());
     }
     return Optional.empty();
   }
@@ -637,14 +652,14 @@
       String replacementLine = lines[i];
       if (matcher.matches() && !NON_REPLACE_ACCOUNT_PATTERN.matcher(matcher.group(2)).matches()) {
         anyFixed = true;
-        replacementLine =
-            String.format(
-                "* %s by %s\n",
-                matcher.group(1),
-                getPossibleAccountReplacement(
-                    changeFixProgress,
-                    Optional.empty(),
-                    getAccountInfoFromNameEmail(matcher.group(2))));
+        Optional<String> reviewerReplacement =
+            getPossibleAccountReplacement(
+                changeFixProgress, Optional.empty(), getAccountInfoFromNameEmail(matcher.group(2)));
+        replacementLine = "* " + matcher.group(1);
+        if (reviewerReplacement.isPresent()) {
+          replacementLine += " by " + reviewerReplacement.get();
+        }
+        replacementLine += "\n";
       }
       fixedLines.append(replacementLine);
     }
@@ -708,11 +723,14 @@
     StringBuffer sb = new StringBuffer();
     while (onAddReviewerMatcher.find()) {
       String reviewerName = normalizeOnCodeOwnerAddReviewerMatch(onAddReviewerMatcher.group(1));
-      String replacementName =
+      Optional<String> replacementName =
           getPossibleAccountReplacement(
               changeFixProgress, Optional.empty(), ParsedAccountInfo.create(reviewerName));
       onAddReviewerMatcher.appendReplacement(
-          sb, replacementName + ", who was added as reviewer owns the following files");
+          sb,
+          replacementName.isPresent()
+              ? replacementName.get() + ", who was added as reviewer owns the following files"
+              : "Added reviewer owns the following files");
     }
     onAddReviewerMatcher.appendTail(sb);
     sb.append("\n");
@@ -1071,19 +1089,20 @@
    * <p>If {@code account} is known, replace with {@link AccountTemplateUtil#getAccountTemplate}.
    * Otherwise, try to guess the correct replacement account for {@code accountName} among {@link
    * ChangeFixProgress#parsedAccounts} that appeared in the change. If this fails {@link
-   * #DEFAULT_ACCOUNT_REPLACEMENT} is applied.
+   * Optional#empty} is returned.
    *
    * @param changeFixProgress see {@link ChangeFixProgress}
    * @param account account that should be used for replacement, if known
    * @param accountInfo {@link ParsedAccountInfo} to replace.
-   * @return replacement for {@code accountName}
+   * @return replacement for {@code accountName} or {@link Optional#empty}, if the replacement could
+   *     not be determined.
    */
-  private String getPossibleAccountReplacement(
+  private Optional<String> getPossibleAccountReplacement(
       ChangeFixProgress changeFixProgress,
       Optional<Account.Id> account,
       ParsedAccountInfo accountInfo) {
     if (account.isPresent()) {
-      return AccountTemplateUtil.getAccountTemplate(account.get());
+      return Optional.of(AccountTemplateUtil.getAccountTemplate(account.get()));
     }
     // Retrieve reviewer accounts from cache and try to match by their name.
     Map<Account.Id, AccountState> missingAccountStateReviewers =
@@ -1129,7 +1148,7 @@
                               e.getValue().get().account().getName(), accountInfo.name()))
               .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, e -> e.getValue().get()));
     }
-    String replacementName = DEFAULT_ACCOUNT_REPLACEMENT;
+    Optional<String> replacementName = Optional.empty();
     if (possibleReplacements.isEmpty()) {
       logger.atWarning().log(
           "Fixing ref %s, could not find reviewer account matching name %s",
@@ -1140,8 +1159,9 @@
           changeFixProgress.changeMetaRef, accountInfo);
     } else {
       replacementName =
-          AccountTemplateUtil.getAccountTemplate(
-              Iterables.getOnlyElement(possibleReplacements.keySet()));
+          Optional.of(
+              AccountTemplateUtil.getAccountTemplate(
+                  Iterables.getOnlyElement(possibleReplacements.keySet())));
     }
     return replacementName;
   }
diff --git a/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java b/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
index 55c27be..de7dd0a 100644
--- a/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
+++ b/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
@@ -58,13 +58,18 @@
     Integer parentNum =
         isInitialCommit(ctx.changeNotes().getProjectName(), targetPatchSet.commitId()) ? 0 : 1;
     try {
-      Map<String, FileDiffOutput> modifiedTargetPatchSet =
+      Map<String, FileDiffOutput> baseVsCurrent =
           diffOperations.listModifiedFilesAgainstParent(
               ctx.changeNotes().getProjectName(), targetPatchSet.commitId(), parentNum);
-      Map<String, FileDiffOutput> modifiedSourcePatchSet =
+      Map<String, FileDiffOutput> baseVsPrior =
           diffOperations.listModifiedFilesAgainstParent(
               ctx.changeNotes().getProjectName(), sourcePatchSet.commitId(), parentNum);
-      return match(modifiedTargetPatchSet, modifiedSourcePatchSet);
+      Map<String, FileDiffOutput> priorVsCurrent =
+          diffOperations.listModifiedFiles(
+              ctx.changeNotes().getProjectName(),
+              sourcePatchSet.commitId(),
+              targetPatchSet.commitId());
+      return match(baseVsCurrent, baseVsPrior, priorVsCurrent);
     } catch (DiffNotAvailableException ex) {
       throw new StorageException(
           "failed to compute difference in files, so won't copy"
@@ -79,16 +84,23 @@
    * {@link ChangeType} matches for each modified file.
    */
   public boolean match(
-      Map<String, FileDiffOutput> modifiedFiles1, Map<String, FileDiffOutput> modifiedFiles2) {
+      Map<String, FileDiffOutput> baseVsCurrent,
+      Map<String, FileDiffOutput> baseVsPrior,
+      Map<String, FileDiffOutput> priorVsCurrent) {
     Set<String> allFiles = new HashSet<>();
-    allFiles.addAll(modifiedFiles1.keySet());
-    allFiles.addAll(modifiedFiles2.keySet());
+    allFiles.addAll(baseVsCurrent.keySet());
+    allFiles.addAll(baseVsPrior.keySet());
     for (String file : allFiles) {
       if (Patch.isMagic(file)) {
         continue;
       }
-      FileDiffOutput fileDiffOutput1 = modifiedFiles1.get(file);
-      FileDiffOutput fileDiffOutput2 = modifiedFiles2.get(file);
+      FileDiffOutput fileDiffOutput1 = baseVsCurrent.get(file);
+      FileDiffOutput fileDiffOutput2 = baseVsPrior.get(file);
+      if (!priorVsCurrent.containsKey(file)) {
+        // If the file is not modified between prior and current patchsets, then scan safely skip
+        // it. The file might has been modified due to rebase.
+        continue;
+      }
       if (fileDiffOutput1 == null || fileDiffOutput2 == null) {
         return false;
       }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index cd9e876..3888679 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -43,6 +43,7 @@
 import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.RawInputUtil;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.LabelId;
 import com.google.gerrit.entities.LabelType;
@@ -557,6 +558,46 @@
   }
 
   @Test
+  public void
+      stickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsModifiedDueToRebase_withoutCopyCondition()
+          throws Exception {
+    try (ProjectConfigUpdate u = updateProject(project)) {
+      u.getConfig()
+          .updateLabelType(
+              LabelId.CODE_REVIEW, b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
+      u.save();
+    }
+    // Create two changes both with the same parent
+    PushOneCommit.Result r = createChange();
+    testRepo.reset("HEAD~1");
+    PushOneCommit.Result r2 = createChange();
+
+    // Modify f.txt in change 1. Approve and submit the first change
+    gApi.changes().id(r.getChangeId()).edit().modifyFile("f.txt", RawInputUtil.create("content"));
+    gApi.changes().id(r.getChangeId()).edit().publish();
+    RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
+    revision.review(ReviewInput.approve().label(LabelId.VERIFIED, 1));
+    revision.submit();
+
+    // Add an approval whose score should be copied on change 2.
+    gApi.changes().id(r2.getChangeId()).current().review(ReviewInput.recommend());
+
+    // Rebase the second change. The rebase adds f1.txt.
+    gApi.changes().id(r2.getChangeId()).rebase();
+
+    // The code-review approval is copied for the second change between PS1 and PS2 since the only
+    // modified file is due to rebase.
+    List<PatchSetApproval> patchSetApprovals =
+        r2.getChange().notes().getApprovalsWithCopied().values().stream()
+            .sorted(comparing(a -> a.patchSetId().get()))
+            .collect(toImmutableList());
+    PatchSetApproval nonCopied = patchSetApprovals.get(0);
+    PatchSetApproval copied = patchSetApprovals.get(1);
+    assertCopied(nonCopied, /* psId= */ 1, LabelId.CODE_REVIEW, (short) 1, false);
+    assertCopied(copied, /* psId= */ 2, LabelId.CODE_REVIEW, (short) 1, true);
+  }
+
+  @Test
   public void stickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsModified_withCopyCondition()
       throws Exception {
     try (ProjectConfigUpdate u = updateProject(project)) {
@@ -1072,17 +1113,9 @@
             .sorted(comparing(a -> a.patchSetId().get()))
             .collect(toImmutableList());
     PatchSetApproval nonCopied = patchSetApprovals.get(0);
-
-    assertThat(nonCopied.patchSetId().get()).isEqualTo(1);
-    assertThat(nonCopied.label()).isEqualTo(LabelId.CODE_REVIEW);
-    assertThat(nonCopied.value()).isEqualTo((short) 1);
-    assertThat(nonCopied.copied()).isFalse();
-
     PatchSetApproval copied = patchSetApprovals.get(1);
-    assertThat(copied.patchSetId().get()).isEqualTo(2);
-    assertThat(copied.label()).isEqualTo(LabelId.CODE_REVIEW);
-    assertThat(copied.value()).isEqualTo((short) 1);
-    assertThat(copied.copied()).isTrue();
+    assertCopied(nonCopied, 1, LabelId.CODE_REVIEW, (short) 1, /* copied= */ false);
+    assertCopied(copied, 2, LabelId.CODE_REVIEW, (short) 1, /* copied= */ true);
   }
 
   @Test
@@ -1311,4 +1344,12 @@
     }
     assertWithMessage(name).that(vote).isEqualTo(expectedVote);
   }
+
+  private void assertCopied(
+      PatchSetApproval approval, int psId, String label, short value, boolean copied) {
+    assertThat(approval.patchSetId().get()).isEqualTo(psId);
+    assertThat(approval.label()).isEqualTo(label);
+    assertThat(approval.value()).isEqualTo(value);
+    assertThat(approval.copied()).isEqualTo(copied);
+  }
 }
diff --git a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
index 19c2bcf..7f16cc4 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
@@ -785,7 +785,7 @@
         .containsExactly(
             "@@ -6 +6 @@\n"
                 + "-Removed Verified+2 by Other Account <other@account.com>\n"
-                + "+Removed Verified+2 by Gerrit Account\n");
+                + "+Removed Verified+2\n");
     BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options);
     assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty();
     assertThat(secondRunResult.refsFailedToFix).isEmpty();
@@ -1671,10 +1671,9 @@
                 + "   * file1.java\n"
                 + "\n<GERRIT_ACCOUNT_2>, who was added as reviewer owns the following files:\n"
                 + "   * file3.js\n"
-                + "\nGerrit Account, who was added as reviewer owns the following files:\n"
+                + "\nAdded reviewer owns the following files:\n"
                 + "   * file4.java\n",
-            "Gerrit Account, who was added as reviewer owns the following files:\n"
-                + "   * file6.java\n",
+            "Added reviewer owns the following files:\n" + "   * file6.java\n",
             "Gerrit Account who was added as reviewer owns the following files:\n"
                 + "   * file1.java\n"
                 + "\n<GERRIT_ACCOUNT_1> who was added as reviewer owns the following files:\n"
@@ -1701,10 +1700,10 @@
                 + "+<GERRIT_ACCOUNT_2>, who was added as reviewer owns the following files:\n"
                 + "@@ -12 +12 @@\n"
                 + "-Missing Reviewer who was added as reviewer owns the following files:\n"
-                + "+Gerrit Account, who was added as reviewer owns the following files:\n",
+                + "+Added reviewer owns the following files:\n",
             "@@ -6 +6 @@\n"
                 + "-Reviewer User who was added as reviewer owns the following files:\n"
-                + "+Gerrit Account, who was added as reviewer owns the following files:\n");
+                + "+Added reviewer owns the following files:\n");
     BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options);
     assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty();
     assertThat(secondRunResult.refsFailedToFix).isEmpty();
@@ -2051,7 +2050,8 @@
         getChangeUpdateBody(
             c,
             String.format(
-                "Assignee changed from: %s to: %s", changeOwner.getName(), otherUser.getName())),
+                "Assignee changed from: %s to: %s",
+                changeOwner.getNameEmail(), otherUser.getNameEmail())),
         getAuthorIdent(otherUser.getAccount()));
     writeUpdate(
         RefNames.changeMetaRef(c.getId()),
@@ -2086,14 +2086,12 @@
                 + "-Assignee added: Change Owner\n"
                 + "+Assignee added: <GERRIT_ACCOUNT_1>\n",
             "@@ -6 +6 @@\n"
-                + "-Assignee changed from: Change Owner to: Other Account\n"
+                + "-Assignee changed from: Change Owner <change@owner.com> to: Other Account <other@account.com>\n"
                 + "+Assignee changed from: <GERRIT_ACCOUNT_1> to: <GERRIT_ACCOUNT_2>\n",
             "@@ -6 +6 @@\n"
                 + "-Assignee deleted: Other Account\n"
                 + "+Assignee deleted: <GERRIT_ACCOUNT_2>\n",
-            "@@ -6 +6 @@\n"
-                + "-Assignee added: Reviewer User\n"
-                + "+Assignee added: Gerrit Account\n");
+            "@@ -6 +6 @@\n" + "-Assignee added: Reviewer User\n" + "+Assignee was added.\n");
     BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options);
     assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty();
     assertThat(secondRunResult.refsFailedToFix).isEmpty();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-image-viewer/gr-image-viewer.ts b/polygerrit-ui/app/elements/diff/gr-diff-image-viewer/gr-image-viewer.ts
index 3a05e7d..32f5f39 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-image-viewer/gr-image-viewer.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-image-viewer/gr-image-viewer.ts
@@ -422,25 +422,23 @@
       />
     `;
 
-    const sourceImageWithHighlight = this.diffHighlightSrc
-      ? html`
-          <div id="source-plus-highlight-container">
-            ${sourceImage}
-            <img
-              id="highlight-image"
-              style="${styleMap({
-                opacity: this.showHighlight ? '1' : '0',
-                // When the highlight layer is not being shown, saving the image or
-                // opening it in a new tab from the context menu, e.g. for external
-                // comparison, should give back the source image, not the highlight
-                // layer.
-                'pointer-events': this.showHighlight ? 'auto' : 'none',
-              })}"
-              src="${this.diffHighlightSrc}"
-            />
-          </div>
-        `
-      : '';
+    const sourceImageWithHighlight = html`
+      <div id="source-plus-highlight-container">
+        ${sourceImage}
+        <img
+          id="highlight-image"
+          style="${styleMap({
+            opacity: this.showHighlight ? '1' : '0',
+            // When the highlight layer is not being shown, saving the image or
+            // opening it in a new tab from the context menu, e.g. for external
+            // comparison, should give back the source image, not the highlight
+            // layer.
+            'pointer-events': this.showHighlight ? 'auto' : 'none',
+          })}"
+          src="${this.diffHighlightSrc}"
+        />
+      </div>
+    `;
 
     const versionExplanation = html`
       <div id="version-explanation">
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index 3b93bea..38f55bd 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -242,7 +242,7 @@
     this.addEventListener(EventType.DIALOG_CHANGE, e => {
       this._handleDialogChange(e as CustomEvent<DialogChangeEventDetail>);
     });
-    this.addEventListener('location-change', e =>
+    this.addEventListener(EventType.LOCATION_CHANGE, e =>
       this._handleLocationChange(e)
     );
     this.addEventListener(EventType.RECREATE_CHANGE_VIEW, () =>
@@ -251,7 +251,7 @@
     this.addEventListener(EventType.RECREATE_DIFF_VIEW, () =>
       this.handleRecreateView(GerritView.DIFF)
     );
-    document.addEventListener('gr-rpc-log', e => this._handleRpcLog(e));
+    document.addEventListener(EventType.GR_RPC_LOG, e => this._handleRpcLog(e));
   }
 
   override ready() {
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts
index ea81aae..5fc53e6 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts
@@ -18,6 +18,8 @@
 import {customElement} from 'lit/decorators';
 import {HovercardMixin} from '../../../mixins/hovercard-mixin/hovercard-mixin';
 import {css, html, LitElement} from 'lit';
+import {hovercardStyles} from '../../../styles/gr-hovercard-styles';
+import {sharedStyles} from '../../../styles/shared-styles';
 
 // This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
 const base = HovercardMixin(LitElement);
@@ -26,7 +28,8 @@
 export class GrHovercard extends base {
   static override get styles() {
     return [
-      super.styles || [],
+      sharedStyles,
+      hovercardStyles,
       css`
         #container {
           padding: var(--spacing-l);