Merge "Auth alert should have the highest priority among all error alerts"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index f320d2a..6da455d 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6827,6 +6827,11 @@
 The source to merge from, e.g. a complete or abbreviated commit SHA-1,
 a complete reference name, a short reference name under `refs/heads`, `refs/tags`,
 or `refs/remotes` namespace, etc.
+|`source_branch`  |optional|
+A branch from which `source` is reachable. If specified,
+`source` is checked for visibility and reachability against only this
+branch. This speeds up the operation, especially for large repos with
+many branches.
 |`strategy`     |optional|
 The strategy of the merge, can be `recursive`, `resolve`,
 `simple-two-way-in-core`, `ours` or `theirs`, default will use project settings.
diff --git a/java/com/google/gerrit/extensions/common/MergeInput.java b/java/com/google/gerrit/extensions/common/MergeInput.java
index c16a551..c3cfcee 100644
--- a/java/com/google/gerrit/extensions/common/MergeInput.java
+++ b/java/com/google/gerrit/extensions/common/MergeInput.java
@@ -24,6 +24,12 @@
   public String source;
 
   /**
+   * If specified, visibility of the {@code source} commit will only be checked against {@code
+   * source_branch}, rather than all visible branches.
+   */
+  public String sourceBranch;
+
+  /**
    * {@code strategy} name of the merge strategy.
    *
    * @see org.eclipse.jgit.merge.MergeStrategy
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index edf8864..7df3902 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -128,6 +128,7 @@
 import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.git.receive.ReceiveCommitsModule;
+import com.google.gerrit.server.git.validators.CommentLimitsValidator;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
 import com.google.gerrit.server.git.validators.MergeValidationListener;
 import com.google.gerrit.server.git.validators.MergeValidators;
@@ -408,6 +409,10 @@
     factory(UploadValidators.Factory.class);
     DynamicSet.setOf(binder(), UploadValidationListener.class);
 
+    bind(CommentValidator.class)
+        .annotatedWith(Exports.named(CommentLimitsValidator.class.getSimpleName()))
+        .to(CommentLimitsValidator.class);
+
     DynamicMap.mapOf(binder(), DynamicOptions.DynamicBean.class);
     DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
     DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
diff --git a/java/com/google/gerrit/server/git/receive/BUILD b/java/com/google/gerrit/server/git/receive/BUILD
index 7402a37..766a835 100644
--- a/java/com/google/gerrit/server/git/receive/BUILD
+++ b/java/com/google/gerrit/server/git/receive/BUILD
@@ -34,7 +34,7 @@
 
 java_library(
     name = "ref_cache",
-    srcs = glob(["ReceivePackRefCache.java"]),
+    srcs = ["ReceivePackRefCache.java"],
     visibility = ["//visibility:public"],
     deps = [
         "//java/com/google/gerrit/entities",
diff --git a/java/com/google/gerrit/server/git/validators/CommentLimitsValidator.java b/java/com/google/gerrit/server/git/validators/CommentLimitsValidator.java
new file mode 100644
index 0000000..8237e69
--- /dev/null
+++ b/java/com/google/gerrit/server/git/validators/CommentLimitsValidator.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 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.git.validators;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentValidationFailure;
+import com.google.gerrit.extensions.validators.CommentValidator;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import org.eclipse.jgit.lib.Config;
+
+/** Limits the size of comments to prevent large comments from causing issues. */
+public class CommentLimitsValidator implements CommentValidator {
+  private final int maxCommentLength;
+
+  @Inject
+  CommentLimitsValidator(@GerritServerConfig Config serverConfig) {
+    maxCommentLength = serverConfig.getInt("change", null, "maxCommentLength", 16 << 10);
+  }
+
+  @Override
+  public ImmutableList<CommentValidationFailure> validateComments(
+      ImmutableList<CommentForValidation> comments) {
+    return comments.stream()
+        .filter(c -> c.getText().length() > maxCommentLength)
+        .map(
+            c ->
+                c.failValidation(
+                    String.format(
+                        "Comment too large (%d > %d)", c.getText().length(), maxCommentLength)))
+        .collect(ImmutableList.toImmutableList());
+  }
+}
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 90d6b66..4159ebb 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -394,7 +394,7 @@
 
     FileCountValidator(PatchListCache patchListCache, Config config) {
       this.patchListCache = patchListCache;
-      maxFileCount = config.getInt("change", null, "maxFiles", 50_000);
+      maxFileCount = config.getInt("change", null, "maxFiles", 100_000);
     }
 
     @Override
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index c0b28d2..537993a 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -508,7 +508,13 @@
     }
 
     RevCommit sourceCommit = MergeUtil.resolveCommit(repo, rw, merge.source);
-    if (!commits.canRead(projectState, repo, sourceCommit)) {
+    if (merge.sourceBranch != null) {
+      Ref ref = repo.findRef(merge.sourceBranch);
+      logger.atFine().log("checking visibility for branch %s", merge.sourceBranch);
+      if (ref == null || !commits.canRead(projectState, repo, sourceCommit, ref)) {
+        throw new BadRequestException("do not have read permission for: " + merge.source);
+      }
+    } else if (!commits.canRead(projectState, repo, sourceCommit)) {
       throw new BadRequestException("do not have read permission for: " + merge.source);
     }
 
diff --git a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
index 31fa8d3..757b650 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -110,6 +111,14 @@
     return views;
   }
 
+  /**
+   * @return true if {@code commit} is visible to the caller and {@code commit} is reachable from
+   *     the given branch.
+   */
+  public boolean canRead(ProjectState state, Repository repo, RevCommit commit, Ref ref) {
+    return reachable.fromRefs(state.getNameKey(), repo, commit, ImmutableList.of(ref));
+  }
+
   /** @return true if {@code commit} is visible to the caller. */
   public boolean canRead(ProjectState state, Repository repo, RevCommit commit) throws IOException {
     Project.NameKey project = state.getNameKey();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index 10bae39..3f80dd1 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -619,6 +619,8 @@
 
     gApi.projects().name(project.get()).branch(mergeTarget).create(branchInput);
 
+    // To create a merge commit, create two changes from the same parent,
+    // and submit them one after the other.
     PushOneCommit.Result result1 =
         pushFactory
             .create(
@@ -668,6 +670,39 @@
     gApi.changes().create(in);
   }
 
+  @Test
+  public void createChangeWithSourceBranch() throws Exception {
+    changeInTwoBranches("branchA", "a.txt", "branchB", "b.txt");
+
+    // create a merge change from branchA to master in gerrit
+    ChangeInput in = new ChangeInput();
+    in.project = project.get();
+    in.branch = "branchA";
+    in.subject = "message";
+    in.status = ChangeStatus.NEW;
+    MergeInput mergeInput = new MergeInput();
+
+    String mergeRev = gApi.projects().name(project.get()).branch("branchB").get().revision;
+    mergeInput.source = mergeRev;
+    in.merge = mergeInput;
+
+    assertCreateSucceeds(in);
+
+    // Succeeds with a visible branch
+    in.merge.sourceBranch = "refs/heads/branchB";
+    gApi.changes().create(in);
+
+    // Make it invisible
+    projectOperations
+        .project(project)
+        .forUpdate()
+        .add(block(READ).ref(in.merge.sourceBranch).group(REGISTERED_USERS))
+        .update();
+
+    // Now it fails.
+    assertThrows(BadRequestException.class, () -> gApi.changes().create(in));
+  }
+
   private ChangeInput newChangeInput(ChangeStatus status) {
     ChangeInput in = new ChangeInput();
     in.project = project.get();
diff --git a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
index 84bf759c..d8b65b7 100644
--- a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.acceptance.server.git.receive;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -24,6 +25,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.PushOneCommit.Result;
+import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.api.changes.DraftInput;
 import com.google.gerrit.extensions.client.Side;
@@ -46,6 +48,8 @@
   @Inject private CommentValidator mockCommentValidator;
   @Inject private TestCommentHelper testCommentHelper;
 
+  private static final int MAX_COMMENT_LENGTH = 666;
+
   private static final String COMMENT_TEXT = "The comment text";
 
   @Captor private ArgumentCaptor<ImmutableList<CommentForValidation>> capture;
@@ -135,4 +139,23 @@
             CommentForValidation.create(
                 CommentForValidation.CommentType.FILE_COMMENT, draftFile.message));
   }
+
+  @Test
+  @GerritConfig(name = "change.maxCommentLength", value = "" + MAX_COMMENT_LENGTH)
+  public void validateComments_enforceLimits_commentTooLarge() throws Exception {
+    when(mockCommentValidator.validateComments(any())).thenReturn(ImmutableList.of());
+    PushOneCommit.Result result = createChange();
+    String changeId = result.getChangeId();
+    int commentLength = MAX_COMMENT_LENGTH + 1;
+    DraftInput comment =
+        testCommentHelper.newDraft(new String(new char[commentLength]).replace("\0", "x"));
+    testCommentHelper.addDraft(changeId, result.getCommit().getName(), comment);
+
+    assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).isEmpty();
+    Result amendResult = amendChange(changeId, "refs/for/master%publish-comments", admin, testRepo);
+    amendResult.assertOkStatus();
+    amendResult.assertMessage(
+        String.format("Comment too large (%d > %d)", commentLength, MAX_COMMENT_LENGTH));
+    assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).isEmpty();
+  }
 }
diff --git a/lib/guava.bzl b/lib/guava.bzl
index 18a8355..86060d4 100644
--- a/lib/guava.bzl
+++ b/lib/guava.bzl
@@ -1,5 +1,5 @@
-GUAVA_VERSION = "28.1-jre"
+GUAVA_VERSION = "28.2-jre"
 
-GUAVA_BIN_SHA1 = "b0e91dcb6a44ffb6221b5027e12a5cb34b841145"
+GUAVA_BIN_SHA1 = "8ec9ed76528425762174f0011ce8f74ad845b756"
 
 GUAVA_DOC_URL = "https://google.github.io/guava/releases/" + GUAVA_VERSION + "/api/docs/"
diff --git a/lib/lucene/BUILD b/lib/lucene/BUILD
index 5ca9580..93eeccb 100644
--- a/lib/lucene/BUILD
+++ b/lib/lucene/BUILD
@@ -1,5 +1,4 @@
 load("@rules_java//java:defs.bzl", "java_binary", "java_import", "java_library")
-load("//tools/bzl:maven.bzl", "merge_maven_jars")
 
 package(default_visibility = ["//visibility:public"])
 
diff --git a/plugins/delete-project b/plugins/delete-project
index 39dd25c..7885a9f 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 39dd25c822be14a69282d38e5205f0ca4f2902c1
+Subproject commit 7885a9fa8d262b6127a7a3aa294108d6aac47a4d
diff --git a/plugins/gitiles b/plugins/gitiles
index 22b8e24..825ca06 160000
--- a/plugins/gitiles
+++ b/plugins/gitiles
@@ -1 +1 @@
-Subproject commit 22b8e242b5eaa9eae817b776bc862b096479ceaa
+Subproject commit 825ca06dddc9de89daa6b126dfc187fbeb25280c
diff --git a/plugins/webhooks b/plugins/webhooks
index 377fec1..570daca 160000
--- a/plugins/webhooks
+++ b/plugins/webhooks
@@ -1 +1 @@
-Subproject commit 377fec17d2c0fc71cdfb1d12f502cfa1eba1fbd7
+Subproject commit 570dacacc64f7c01e0bc0f1301aa1d5a218cfc1b
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
index 609a00d..1df2a41 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
@@ -245,31 +245,43 @@
       });
     });
 
-    test('_getAccountSuggestions empty', () => element
-        ._getAccountSuggestions('nonexistent').then(accounts => {
-          assert.equal(accounts.length, 0);
-        }));
+    test('_getAccountSuggestions empty', done => {
+      element
+          ._getAccountSuggestions('nonexistent').then(accounts => {
+            assert.equal(accounts.length, 0);
+            done();
+          });
+    });
 
-    test('_getAccountSuggestions non-empty', () => element
-        ._getAccountSuggestions('test-').then(accounts => {
-          assert.equal(accounts.length, 3);
-          assert.equal(accounts[0].name,
-              'test-account <test.account@example.com>');
-          assert.equal(accounts[1].name, 'test-admin <test.admin@example.com>');
-          assert.equal(accounts[2].name, 'test-git');
-        }));
+    test('_getAccountSuggestions non-empty', done => {
+      element
+          ._getAccountSuggestions('test-').then(accounts => {
+            assert.equal(accounts.length, 3);
+            assert.equal(accounts[0].name,
+                'test-account <test.account@example.com>');
+            assert.equal(accounts[1].name, 'test-admin <test.admin@example.com>');
+            assert.equal(accounts[2].name, 'test-git');
+            done();
+          });
+    });
 
-    test('_getGroupSuggestions empty', () => element
-        ._getGroupSuggestions('nonexistent').then(groups => {
-          assert.equal(groups.length, 0);
-        }));
+    test('_getGroupSuggestions empty', done => {
+      element
+          ._getGroupSuggestions('nonexistent').then(groups => {
+            assert.equal(groups.length, 0);
+            done();
+          });
+    });
 
-    test('_getGroupSuggestions non-empty', () => element
-        ._getGroupSuggestions('test').then(groups => {
-          assert.equal(groups.length, 2);
-          assert.equal(groups[0].name, 'test-admin');
-          assert.equal(groups[1].name, 'test/Administrator (admin)');
-        }));
+    test('_getGroupSuggestions non-empty', done => {
+      element
+          ._getGroupSuggestions('test').then(groups => {
+            assert.equal(groups.length, 2);
+            assert.equal(groups[0].name, 'test-admin');
+            assert.equal(groups[1].name, 'test/Administrator (admin)');
+            done();
+          });
+    });
 
     test('_computeHideItemClass returns string for admin', () => {
       const admin = true;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
index 6f68525..7b79a7e3 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
@@ -307,13 +307,18 @@
         });
       });
 
-      test('inherited submit type value is calculated correctly', () => element
-          ._loadRepo().then(() => {
-            const sel = element.$.submitTypeSelect;
-            assert.equal(sel.bindValue, 'INHERIT');
-            assert.equal(
-                sel.nativeSelect.options[0].text, 'Inherit (Merge if necessary)');
-          }));
+      test('inherited submit type value is calculated correctly', done => {
+        element
+            ._loadRepo().then(() => {
+              const sel = element.$.submitTypeSelect;
+              assert.equal(sel.bindValue, 'INHERIT');
+              assert.equal(
+                  sel.nativeSelect.options[0].text,
+                  'Inherit (Merge if necessary)'
+              );
+              done();
+            });
+      });
 
       test('fields update and save correctly', () => {
         const configInputObj = {
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index ec5c5a5..1c894ee 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -1454,14 +1454,17 @@
               'navigateToChange').returns(Promise.resolve(true));
         });
 
-        test('change action', () => element
-            ._send('DELETE', payload, '/endpoint', false, cleanup)
-            .then(() => {
-              assert.isFalse(onShowError.called);
-              assert.isTrue(cleanup.calledOnce);
-              assert.isTrue(sendStub.calledWith(42, 'DELETE', '/endpoint',
-                  null, payload));
-            }));
+        test('change action', done => {
+          element
+              ._send('DELETE', payload, '/endpoint', false, cleanup)
+              .then(() => {
+                assert.isFalse(onShowError.called);
+                assert.isTrue(cleanup.calledOnce);
+                assert.isTrue(sendStub.calledWith(42, 'DELETE', '/endpoint',
+                    null, payload));
+                done();
+              });
+        });
 
         suite('show revert submission dialog', () => {
           setup(() => {
@@ -1545,14 +1548,17 @@
           });
         });
 
-        test('revision action', () => element
-            ._send('DELETE', payload, '/endpoint', true, cleanup)
-            .then(() => {
-              assert.isFalse(onShowError.called);
-              assert.isTrue(cleanup.calledOnce);
-              assert.isTrue(sendStub.calledWith(42, 'DELETE', '/endpoint',
-                  12, payload));
-            }));
+        test('revision action', done => {
+          element
+              ._send('DELETE', payload, '/endpoint', true, cleanup)
+              .then(() => {
+                assert.isFalse(onShowError.called);
+                assert.isTrue(cleanup.calledOnce);
+                assert.isTrue(sendStub.calledWith(42, 'DELETE', '/endpoint',
+                    12, payload));
+                done();
+              });
+        });
       });
 
       suite('failure modes', () => {
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
index 8ef561c..277bb78 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
@@ -336,12 +336,16 @@
     });
   });
 
-  test('openOpenDialog', () => element.openOpenDialog('test/path.cpp')
-      .then(() => {
-        assert.isFalse(element.$.openDialog.hasAttribute('hidden'));
-        assert.equal(element.$.openDialog.querySelector('gr-autocomplete').text,
-            'test/path.cpp');
-      }));
+  test('openOpenDialog', done => {
+    element.openOpenDialog('test/path.cpp')
+        .then(() => {
+          assert.isFalse(element.$.openDialog.hasAttribute('hidden'));
+          assert.equal(
+              element.$.openDialog.querySelector('gr-autocomplete').text,
+              'test/path.cpp');
+          done();
+        });
+  });
 
   test('_getDialogFromEvent', () => {
     const spy = sandbox.spy(element, '_getDialogFromEvent');
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
index 8eabe2a..e6f446b 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
@@ -56,9 +56,12 @@
       assert.isOk(element);
     });
 
-    test('open uses open() from gr-overlay', () => element.open().then(() => {
-      assert.isTrue(element.$.overlay.open.called);
-    }));
+    test('open uses open() from gr-overlay', done => {
+      element.open().then(() => {
+        assert.isTrue(element.$.overlay.open.called);
+        done();
+      });
+    });
 
     test('close uses close() from gr-overlay', () => {
       element.close();
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
index af3d770..55b7ac5 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
@@ -69,22 +69,28 @@
         instance = new GrPopupInterface(plugin);
       });
 
-      test('open', () => instance.open().then(api => {
-        assert.strictEqual(api, instance);
-        const manual = document.createElement('div');
-        manual.id = 'foobar';
-        manual.innerHTML = 'manual content';
-        api._getElement().appendChild(manual);
-        flushAsynchronousOperations();
-        assert.equal(
-            container.querySelector('#foobar').textContent, 'manual content');
-      }));
+      test('open', done => {
+        instance.open().then(api => {
+          assert.strictEqual(api, instance);
+          const manual = document.createElement('div');
+          manual.id = 'foobar';
+          manual.innerHTML = 'manual content';
+          api._getElement().appendChild(manual);
+          flushAsynchronousOperations();
+          assert.equal(
+              container.querySelector('#foobar').textContent, 'manual content');
+          done();
+        });
+      });
 
-      test('close', () => instance.open().then(api => {
-        assert.isTrue(api._getElement().node.opened);
-        api.close();
-        assert.isFalse(api._getElement().node.opened);
-      }));
+      test('close', done => {
+        instance.open().then(api => {
+          assert.isTrue(api._getElement().node.opened);
+          api.close();
+          assert.isFalse(api._getElement().node.opened);
+          done();
+        });
+      });
     });
 
     suite('components', () => {
@@ -92,16 +98,22 @@
         instance = new GrPopupInterface(plugin, 'gr-user-test-popup');
       });
 
-      test('open', () => instance.open().then(api => {
-        assert.isNotNull(
-            Polymer.dom(container).querySelector('gr-user-test-popup'));
-      }));
+      test('open', done => {
+        instance.open().then(api => {
+          assert.isNotNull(
+              Polymer.dom(container).querySelector('gr-user-test-popup'));
+          done();
+        });
+      });
 
-      test('close', () => instance.open().then(api => {
-        assert.isTrue(api._getElement().node.opened);
-        api.close();
-        assert.isFalse(api._getElement().node.opened);
-      }));
+      test('close', done => {
+        instance.open().then(api => {
+          assert.isTrue(api._getElement().node.opened);
+          api.close();
+          assert.isFalse(api._getElement().node.opened);
+          done();
+        });
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
index adc4f68..3bad2a9 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
@@ -126,16 +126,19 @@
             });
       });
 
-      test('does not query when repo is unset', () => element
-          ._getRepoBranchesSuggestions('')
-          .then(() => {
-            assert.isFalse(element.$.restAPI.getRepoBranches.called);
-            element.repo = 'gerrit';
-            return element._getRepoBranchesSuggestions('');
-          })
-          .then(() => {
-            assert.isTrue(element.$.restAPI.getRepoBranches.called);
-          }));
+      test('does not query when repo is unset', done => {
+        element
+            ._getRepoBranchesSuggestions('')
+            .then(() => {
+              assert.isFalse(element.$.restAPI.getRepoBranches.called);
+              element.repo = 'gerrit';
+              return element._getRepoBranchesSuggestions('');
+            })
+            .then(() => {
+              assert.isTrue(element.$.restAPI.getRepoBranches.called);
+              done();
+            });
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/styles/themes/app-theme.html b/polygerrit-ui/app/styles/themes/app-theme.html
index 0dec63f..369af2b 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.html
+++ b/polygerrit-ui/app/styles/themes/app-theme.html
@@ -59,17 +59,15 @@
   --view-background-color: var(--background-color-primary);
   /* unique background colors */
   --assignee-highlight-color: #fcfad6;
+  --comment-background-color: #fcfad6;
+  --robot-comment-background-color: #e8f0fe;
   --edit-mode-background-color: #ebf5fb;
   --emphasis-color: #fff9c4;
   --hover-background-color: rgba(161, 194, 250, 0.2);
   --primary-button-background-color: #2a66d9;
   --selection-background-color: rgba(161, 194, 250, 0.1);
   --tooltip-background-color: #333;
-  /* comment background colors */
-  --comment-background-color: #fef7f0;
-  --robot-comment-background-color: #e8f0fe;
-  --unresolved-comment-background-color: #e8eaed;
-  /* vote background colors */
+  --unresolved-comment-background-color: #fcfaa6;
   --vote-color-approved: #9fcc6b;
   --vote-color-disliked: #f7c4cb;
   --vote-color-neutral: #ebf5fb;
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index 57ceb32..80243dc 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -49,17 +49,15 @@
       /*   empty, because inheriting from app-theme is just fine
       /* unique background colors */
       --assignee-highlight-color: #3a361c;
+      --comment-background-color: #0b162b;
+      --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);
       --selection-background-color: rgba(161, 194, 250, 0.1);
       --tooltip-background-color: #111;
-      /* comment background colors */
-      --comment-background-color: #303134;
-      --robot-comment-background-color: #40454e;
-      --unresolved-comment-background-color: #4b463a;
-      /* vote background colors */
+      --unresolved-comment-background-color: #385a9a;
       --vote-color-approved: #7fb66b;
       --vote-color-disliked: #bf6874;
       --vote-color-neutral: #597280;
diff --git a/tools/bzl/maven.bzl b/tools/bzl/maven.bzl
deleted file mode 100644
index 36e3084e..0000000
--- a/tools/bzl/maven.bzl
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Merge maven files
-
-load("@rules_java//java:defs.bzl", "java_import")
-
-def cmd(jars):
-    return ("$(location //tools:merge_jars) $@ " +
-            " ".join(["$(location %s)" % j for j in jars]))
-
-def merge_maven_jars(name, srcs, **kwargs):
-    native.genrule(
-        name = "%s__merged_bin" % name,
-        cmd = cmd(srcs),
-        tools = srcs + ["//tools:merge_jars"],
-        outs = ["%s__merged.jar" % name],
-    )
-    java_import(
-        name = name,
-        jars = [":%s__merged_bin" % name],
-        **kwargs
-    )
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 7f0e88b..9915a6e 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -239,7 +239,7 @@
             if p.endswith('libquery_parser.jar') or \
                p.endswith('libgerrit-prolog-common.jar') or \
                p.endswith('com_google_protobuf/libprotobuf_java.jar') or \
-               p.endswith('lucene-core-and-backward-codecs__merged.jar'):
+               p.endswith('lucene-core-and-backward-codecs-merged_deploy.jar'):
                 lib.add(p)
             if proto_library.match(p) :
                 proto.add(p)