Merge changes Id6a89e15,I2c98ee86,I34c1558b

* changes:
  Extract RepoSequence into an interface and an implementation
  Move RepoSequence#storeNew method to AllProjectsCreator
  Remove unused constructor in RepoSequence
diff --git a/Documentation/backend_licenses.txt b/Documentation/backend_licenses.txt
index 45da2ba..9ea01cf 100755
--- a/Documentation/backend_licenses.txt
+++ b/Documentation/backend_licenses.txt
@@ -1113,28 +1113,7 @@
 [[flexmark]]
 flexmark
 
-* flexmark
-* flexmark-ext-abbreviation
-* flexmark-ext-anchorlink
-* flexmark-ext-autolink
-* flexmark-ext-definition
-* flexmark-ext-emoji
-* flexmark-ext-escaped-character
-* flexmark-ext-footnotes
-* flexmark-ext-gfm-issues
-* flexmark-ext-gfm-strikethrough
-* flexmark-ext-gfm-tasklist
-* flexmark-ext-gfm-users
-* flexmark-ext-ins
-* flexmark-ext-jekyll-front-matter
-* flexmark-ext-superscript
-* flexmark-ext-tables
-* flexmark-ext-toc
-* flexmark-ext-typographic
-* flexmark-ext-wikilink
-* flexmark-ext-yaml-front-matter
-* flexmark-profile-pegdown
-* flexmark-util
+* flexmark-all-lib
 
 [[flexmark_license]]
 ----
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index 43019dc..6c3c459 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -1119,28 +1119,7 @@
 [[flexmark]]
 flexmark
 
-* flexmark
-* flexmark-ext-abbreviation
-* flexmark-ext-anchorlink
-* flexmark-ext-autolink
-* flexmark-ext-definition
-* flexmark-ext-emoji
-* flexmark-ext-escaped-character
-* flexmark-ext-footnotes
-* flexmark-ext-gfm-issues
-* flexmark-ext-gfm-strikethrough
-* flexmark-ext-gfm-tasklist
-* flexmark-ext-gfm-users
-* flexmark-ext-ins
-* flexmark-ext-jekyll-front-matter
-* flexmark-ext-superscript
-* flexmark-ext-tables
-* flexmark-ext-toc
-* flexmark-ext-typographic
-* flexmark-ext-wikilink
-* flexmark-ext-yaml-front-matter
-* flexmark-profile-pegdown
-* flexmark-util
+* flexmark-all-lib
 
 [[flexmark_license]]
 ----
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index 0d72447..a560b84 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -279,6 +279,9 @@
    view implementation class
 * `http/server/rest_api/change_json/to_change_info_latency`: Latency for
   toChangeInfo invocations in ChangeJson.
+* `http/server/rest_api/change_json/to_change_info_latency/parent_data_computation`:
+   Latency for computing parent data information in toRevisionInfo invocations
+   in RevisionJson. See link:rest-api-changes.html#parent-info[ParentInfo].
 * `http/server/rest_api/change_json/to_change_infos_latency`: Latency for
   toChangeInfos invocations in ChangeJson.
 * `http/server/rest_api/change_json/format_query_results_latency`: Latency for
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index ea95680..5026c5f 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1472,6 +1472,9 @@
 Optionally, the parent revision (of the oldest ancestor to be rebased) can be changed to another
 change, revision or branch through the link:#rebase-input[RebaseInput] entity.
 
+Providing a `committer_email` through the link:#rebase-input[RebaseInput] entity is not supported
+when rebasing a chain.
+
 If the chain is outdated, i.e., there's a change that depends on an old revision of its parent, the
 result is the same as individually rebasing all outdated changes on top of their parent's latest
 revision before running the rebase chain action.
@@ -8343,6 +8346,10 @@
 In addition, rebasing on behalf of the uploader is only supported for the
 current patch set of a change. +
 If the caller is the uploader this flag is ignored and a normal rebase is done.
+|`committer_email`|optional|
+Rebase is committed using this email address. Only the registered emails
+of the calling user or uploader (when `on_behalf_of_uploader` is `true`) are
+considered valid. This option is not supported when rebasing a chain.
 |`validation_options`   |optional|
 Map with key-value pairs that are forwarded as options to the commit validation
 listeners (e.g. can be used to skip certain validations). Which validation
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 9ed4792..cc020ad 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -345,7 +345,7 @@
 of the argument.
 
 [[message]]
-message:'MESSAGE'::
+message:'MESSAGE'::, description:'MESSAGE'::, d:'MESSAGE'::
 +
 Changes that match 'MESSAGE' arbitrary string in the commit message body.
 By default full text matching is used, but regular expressions can be
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 0539582..80582a4 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -285,7 +285,9 @@
   @Inject protected TestTicker testTicker;
 
   protected EventRecorder eventRecorder;
+
   protected GerritServer server;
+
   protected Project.NameKey project;
   protected RestSession adminRestSession;
   protected RestSession userRestSession;
@@ -295,7 +297,6 @@
   protected TestAccount admin;
   protected TestAccount user;
   protected TestRepository<InMemoryRepository> testRepo;
-  protected String testMethodName;
   protected String resourcePrefix;
   protected Description description;
   protected GerritServer.Description testMethodDescription;
@@ -318,6 +319,101 @@
   private String systemTimeZone;
   private SystemReader oldSystemReader;
 
+  /**
+   * The Getters and Setters below are needed for tests that run on custom {@link GerritServer}
+   * (that can be set up via {@link #initServer} and {@link #setUpDatabase} methods. Because tests
+   * inherit directly from {@link AbstractDaemonTest}, the set up has to be delegated to some other
+   * class that can share the set up logic across different test classes.
+   *
+   * <p>E.g, we need to be able to do something like:
+   *
+   * <pre>{@code
+   * public class AccountIT extends AbstractDaemonTest {...}
+   *
+   * public class AbstractDaemonTestAdapter {
+   *
+   *   protected void initServer() {...}
+   *
+   *   ...
+   *
+   * }
+   *
+   * public class CustomAccountIT extends AccountIT {
+   *
+   *   AbstractDaemonTestAdapter testAdapter;
+   *
+   *   {@literal @Override}
+   *   protected void initServer() {
+   *         testAdapter.initServer();
+   *   }
+   *   ...
+   * }
+   *
+   * public class CustomChangeIT extends ChangeIT {
+   *
+   *   AbstractDaemonTestAdapter testAdapter;
+   *
+   *   {@literal @Override}
+   *   protected void initServer() {
+   *         testAdapter.initServer();
+   *   }
+   *   ...
+   * }
+   *
+   * }</pre>
+   */
+  public String getResourcePrefix() {
+    return resourcePrefix;
+  }
+
+  public void setResourcePrefix(String resourcePrefix) {
+    this.resourcePrefix = resourcePrefix;
+  }
+
+  public Description getDescription() {
+    return description;
+  }
+
+  public TestRepository<InMemoryRepository> getTestRepo() {
+    return testRepo;
+  }
+
+  public void setTestRepo(TestRepository<InMemoryRepository> testRepo) {
+    this.testRepo = testRepo;
+  }
+
+  public TestAccount getUser() {
+    return user;
+  }
+
+  public void setUser(TestAccount user) {
+    this.user = user;
+  }
+
+  public TestAccount getAdmin() {
+    return admin;
+  }
+
+  public void setAdmin(TestAccount admin) {
+    this.admin = admin;
+  }
+
+  public Project.NameKey getProject() {
+    return project;
+  }
+
+  public void setProject(Project.NameKey project) {
+    this.project = project;
+  }
+
+  public GerritServer getServer() {
+    return server;
+  }
+
+  public void setServer(GerritServer server) {
+    this.server = server;
+  }
+
   @Before
   public void clearSender() {
     if (sender != null) {
@@ -409,7 +505,7 @@
     initSsh();
   }
 
-  protected void reindexAccount(Account.Id accountId) {
+  public void reindexAccount(Account.Id accountId) {
     accountIndexer.index(accountId);
   }
 
@@ -486,14 +582,13 @@
 
     initSsh();
 
-    testMethodName = description.getMethodName();
+    String testMethodName = description.getMethodName();
     resourcePrefix =
         UNSAFE_PROJECT_NAME
             .matcher(description.getClassName() + "_" + testMethodName + "_")
             .replaceAll("");
 
-    Context ctx = newRequestContext(admin);
-    atrScope.set(ctx);
+    setRequestScope(admin);
     ProjectInput in = projectInput(description);
     gApi.projects().create(in);
     project = Project.nameKey(in.name);
@@ -606,7 +701,7 @@
   }
 
   /** Generate default project properties based on test description */
-  protected ProjectInput projectInput(Description description) {
+  public ProjectInput projectInput(Description description) {
     ProjectInput in = new ProjectInput();
     TestProjectInput ann = description.getAnnotation(TestProjectInput.class);
     in.name = name("project");
@@ -639,7 +734,7 @@
     // Default implementation does nothing.
   }
 
-  protected static final Pattern UNSAFE_PROJECT_NAME = Pattern.compile("[^a-zA-Z0-9._/-]+");
+  public static final Pattern UNSAFE_PROJECT_NAME = Pattern.compile("[^a-zA-Z0-9._/-]+");
 
   protected Git git() {
     return testRepo.git();
@@ -1063,6 +1158,12 @@
     return gApi.changes().query(q).get();
   }
 
+  /** Sets up {@code account} as a caller in tests. */
+  public void setRequestScope(TestAccount account) {
+    Context ctx = newRequestContext(account);
+    atrScope.set(ctx);
+  }
+
   protected Context newRequestContext(TestAccount account) {
     requestScopeOperations.setApiUser(account.id());
     return atrScope.get();
diff --git a/java/com/google/gerrit/extensions/api/changes/RebaseInput.java b/java/com/google/gerrit/extensions/api/changes/RebaseInput.java
index 07e65d0..42dea8d 100644
--- a/java/com/google/gerrit/extensions/api/changes/RebaseInput.java
+++ b/java/com/google/gerrit/extensions/api/changes/RebaseInput.java
@@ -51,4 +51,12 @@
   public boolean onBehalfOfUploader;
 
   public Map<String, String> validationOptions;
+
+  /**
+   * Rebase will be committed using this email address. Only the registered emails of the calling
+   * user or uploader (when onBehalfOfUploader is true) are considered valid.
+   *
+   * <p>This option is not supported when rebasing a chain.
+   */
+  public String committerEmail;
 }
diff --git a/java/com/google/gerrit/httpd/UrlModule.java b/java/com/google/gerrit/httpd/UrlModule.java
index 69adf82..abca415 100644
--- a/java/com/google/gerrit/httpd/UrlModule.java
+++ b/java/com/google/gerrit/httpd/UrlModule.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.httpd;
 
+import static com.google.gerrit.httpd.raw.StaticModule.CHANGE_NUMBER_URI_REGEX;
 import static com.google.inject.Scopes.SINGLETON;
 
 import com.google.common.base.Strings;
@@ -72,7 +73,7 @@
     serveRegex("^/settings/?$").with(screen(PageLinks.SETTINGS));
     serveRegex("^/register$").with(registerScreen(false));
     serveRegex("^/register/(.+)$").with(registerScreen(true));
-    serveRegex("^/([1-9][0-9]*)/?$").with(NumericChangeIdRedirectServlet.class);
+    serveRegex(CHANGE_NUMBER_URI_REGEX).with(NumericChangeIdRedirectServlet.class);
     serveRegex("^/p/(.*)$").with(queryProjectNew());
     serveRegex("^/r/(.+)/?$").with(DirectChangeByCommit.class);
 
diff --git a/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java b/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java
index 36fa61b..4c42e79 100644
--- a/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java
+++ b/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java
@@ -95,13 +95,15 @@
           ListChangesOption.ALL_COMMITS,
           ListChangesOption.ALL_REVISIONS,
           ListChangesOption.CHANGE_ACTIONS,
+          ListChangesOption.DETAILED_ACCOUNTS,
           ListChangesOption.DETAILED_LABELS,
           ListChangesOption.DOWNLOAD_COMMANDS,
           ListChangesOption.MESSAGES,
           ListChangesOption.SUBMITTABLE,
           ListChangesOption.WEB_LINKS,
           ListChangesOption.SKIP_DIFFSTAT,
-          ListChangesOption.SUBMIT_REQUIREMENTS);
+          ListChangesOption.SUBMIT_REQUIREMENTS,
+          ListChangesOption.PARENTS);
 
   @Nullable
   public static String getPath(@Nullable String requestedURL) throws URISyntaxException {
diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
index 3c8287f..587d82a 100644
--- a/java/com/google/gerrit/httpd/raw/StaticModule.java
+++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
@@ -46,6 +46,7 @@
 import java.io.IOException;
 import java.nio.file.FileSystem;
 import java.nio.file.Path;
+import java.util.regex.Pattern;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -61,6 +62,8 @@
 
 public class StaticModule extends ServletModule {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+  public static final String CHANGE_NUMBER_URI_REGEX = "^(?:/c)?/([1-9][0-9]*)/?$";
+  private static final Pattern CHANGE_NUMBER_URI_PATTERN = Pattern.compile(CHANGE_NUMBER_URI_REGEX);
 
   /**
    * Paths that should be treated as static assets when serving PolyGerrit.
@@ -403,7 +406,11 @@
     }
 
     private static boolean isPolyGerritIndex(String path) {
-      return matchPath(POLYGERRIT_INDEX_PATHS, path);
+      return !isChangeNumberUri(path) && matchPath(POLYGERRIT_INDEX_PATHS, path);
+    }
+
+    private static boolean isChangeNumberUri(String path) {
+      return CHANGE_NUMBER_URI_PATTERN.matcher(path).matches();
     }
 
     private static boolean matchPath(Iterable<String> paths, String path) {
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index 3587342..000f095 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -71,28 +71,7 @@
         "//lib:autolink",
         "//lib:automaton",
         "//lib:blame-cache",
-        "//lib:flexmark",
-        "//lib:flexmark-ext-abbreviation",
-        "//lib:flexmark-ext-anchorlink",
-        "//lib:flexmark-ext-autolink",
-        "//lib:flexmark-ext-definition",
-        "//lib:flexmark-ext-emoji",
-        "//lib:flexmark-ext-escaped-character",
-        "//lib:flexmark-ext-footnotes",
-        "//lib:flexmark-ext-gfm-issues",
-        "//lib:flexmark-ext-gfm-strikethrough",
-        "//lib:flexmark-ext-gfm-tasklist",
-        "//lib:flexmark-ext-gfm-users",
-        "//lib:flexmark-ext-ins",
-        "//lib:flexmark-ext-jekyll-front-matter",
-        "//lib:flexmark-ext-superscript",
-        "//lib:flexmark-ext-tables",
-        "//lib:flexmark-ext-toc",
-        "//lib:flexmark-ext-typographic",
-        "//lib:flexmark-ext-wikilink",
-        "//lib:flexmark-ext-yaml-front-matter",
-        "//lib:flexmark-profile-pegdown",
-        "//lib:flexmark-util",
+        "//lib:flexmark-all-lib",
         "//lib:gson",
         "//lib:guava",
         "//lib:guava-retrying",
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index c267822..65e9d9d 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -26,7 +26,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Streams;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.common.UsedAt.Project;
 import com.google.gerrit.entities.Account;
@@ -301,7 +300,7 @@
   private abstract class AccountIdSearcher implements Searcher<Account.Id> {
     @Override
     public final Stream<AccountState> search(Account.Id input) {
-      return Streams.stream(accountCache.get(input));
+      return accountCache.get(input).stream();
     }
   }
 
@@ -375,7 +374,7 @@
 
     @Override
     public Stream<AccountState> search(String input) {
-      return Streams.stream(accountCache.getByUsername(input));
+      return accountCache.getByUsername(input).stream();
     }
 
     @Override
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index 716295f..f32b2eb 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -693,20 +693,20 @@
     }
     return Streams.concat(
             reviewerInputs.stream(),
-            Streams.stream(
-                newReviewerInputFromCommitIdentity(
-                    change,
-                    patchSetInfo.getCommitId(),
-                    patchSetInfo.getAuthor().getAccount(),
-                    NotifyHandling.NONE,
-                    change.getOwner())),
-            Streams.stream(
-                newReviewerInputFromCommitIdentity(
-                    change,
-                    patchSetInfo.getCommitId(),
-                    patchSetInfo.getCommitter().getAccount(),
-                    NotifyHandling.NONE,
-                    change.getOwner())))
+            newReviewerInputFromCommitIdentity(
+                change,
+                patchSetInfo.getCommitId(),
+                patchSetInfo.getAuthor().getAccount(),
+                NotifyHandling.NONE,
+                change.getOwner())
+                .stream(),
+            newReviewerInputFromCommitIdentity(
+                change,
+                patchSetInfo.getCommitId(),
+                patchSetInfo.getCommitter().getAccount(),
+                NotifyHandling.NONE,
+                change.getOwner())
+                .stream())
         .collect(toImmutableList());
   }
 }
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index d83962f..4ab9d5a 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -760,14 +760,22 @@
     // it will be passed to ActionVisitors as-is.
     if (needRevisions) {
       out.revisions = revisionJson.getRevisions(accountLoader, cd, src, limitToPsId, out);
-      if (out.revisions != null) {
-        for (Map.Entry<String, RevisionInfo> entry : out.revisions.entrySet()) {
-          if (entry.getValue().isCurrent) {
-            out.currentRevision = entry.getKey();
-            break;
-          }
+      for (Map.Entry<String, RevisionInfo> entry : out.revisions.entrySet()) {
+        if (entry.getValue().isCurrent) {
+          out.currentRevision = entry.getKey();
+          break;
         }
       }
+      if (out.currentRevision == null) {
+        logger.atSevere().log(
+            "current revision for change %s not found"
+                + "(current patch set ID = %s, patch sets = %s, meta revision = %s)",
+            cd.getId(),
+            cd.change().currentPatchSetId(),
+            src.entrySet().stream()
+                .collect(toImmutableMap(Map.Entry::getKey, e -> e.getValue().commitId().name())),
+            getMetaRevisionIfAvailable(cd));
+      }
     }
 
     if (has(CURRENT_ACTIONS) || has(CHANGE_ACTIONS)) {
@@ -785,6 +793,14 @@
     return out;
   }
 
+  private Optional<ObjectId> getMetaRevisionIfAvailable(ChangeData cd) {
+    try {
+      return Optional.of(cd.metaRevisionOrThrow());
+    } catch (Exception e) {
+      return Optional.empty();
+    }
+  }
+
   private Map<ReviewerState, Collection<AccountInfo>> reviewerMap(
       ReviewerSet reviewers, ReviewerByEmailSet reviewersByEmail, boolean includeRemoved) {
     Map<ReviewerState, Collection<AccountInfo>> reviewerMap = new HashMap<>();
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index f46196f..de3b7d5 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -400,6 +400,10 @@
     return rebasedCommit;
   }
 
+  public PatchSet getOriginalPatchSet() {
+    return originalPatchSet;
+  }
+
   public PatchSet.Id getPatchSetId() {
     checkState(rebasedPatchSetId != null, "getPatchSetId() only valid after updateRepo");
     return rebasedPatchSetId;
diff --git a/java/com/google/gerrit/server/change/RebaseUtil.java b/java/com/google/gerrit/server/change/RebaseUtil.java
index 48b052f..47a1e11 100644
--- a/java/com/google/gerrit/server/change/RebaseUtil.java
+++ b/java/com/google/gerrit/server/change/RebaseUtil.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.change;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.Nullable;
@@ -38,6 +39,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.ChangePermission;
+import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
@@ -45,6 +47,7 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -62,39 +65,38 @@
   private final Provider<PersonIdent> serverIdent;
   private final IdentifiedUser.GenericFactory userFactory;
   private final PermissionBackend permissionBackend;
-  private final ChangeResource.Factory changeResourceFactory;
   private final GitRepositoryManager repoManager;
   private final Provider<InternalChangeQuery> queryProvider;
   private final ChangeNotes.Factory notesFactory;
   private final PatchSetUtil psUtil;
   private final RebaseChangeOp.Factory rebaseFactory;
+  private final Provider<CurrentUser> self;
 
   @Inject
   RebaseUtil(
       @GerritPersonIdent Provider<PersonIdent> serverIdent,
       IdentifiedUser.GenericFactory userFactory,
       PermissionBackend permissionBackend,
-      ChangeResource.Factory changeResourceFactory,
       GitRepositoryManager repoManager,
       Provider<InternalChangeQuery> queryProvider,
       ChangeNotes.Factory notesFactory,
       PatchSetUtil psUtil,
-      RebaseChangeOp.Factory rebaseFactory) {
+      RebaseChangeOp.Factory rebaseFactory,
+      Provider<CurrentUser> self) {
     this.serverIdent = serverIdent;
     this.userFactory = userFactory;
     this.permissionBackend = permissionBackend;
-    this.changeResourceFactory = changeResourceFactory;
     this.repoManager = repoManager;
     this.queryProvider = queryProvider;
     this.notesFactory = notesFactory;
     this.psUtil = psUtil;
     this.rebaseFactory = rebaseFactory;
+    this.self = self;
   }
 
   /**
-   * Checks that the uploader has permissions to create a new patch set and creates a new {@link
-   * RevisionResource} that contains the uploader (aka the impersonated user) as the current user
-   * which can be used for {@link BatchUpdate} to do the rebase on behalf of the uploader.
+   * Checks that the uploader has permissions to create a new patch set as the current user which
+   * can be used for {@link BatchUpdate} to do the rebase on behalf of the uploader.
    *
    * <p>The following permissions are required for the uploader:
    *
@@ -137,10 +139,8 @@
    *
    * @param rsrc the revision resource that should be rebased
    * @param rebaseInput the request input containing options for the rebase
-   * @return revision resource that contains the uploader (aka the impersonated user) as the current
-   *     user which can be used for {@link BatchUpdate} to do the rebase on behalf of the uploader
    */
-  public RevisionResource onBehalfOf(RevisionResource rsrc, RebaseInput rebaseInput)
+  public void checkCanRebaseOnBehalfOf(RevisionResource rsrc, RebaseInput rebaseInput)
       throws IOException, PermissionBackendException, BadRequestException,
           ResourceConflictException {
     if (rebaseInput.allowConflicts) {
@@ -208,9 +208,6 @@
         }
       }
     }
-
-    return new RevisionResource(
-        changeResourceFactory.create(rsrc.getNotes(), uploader), rsrc.getPatchSet());
   }
 
   private void checkPermissionForUploader(
@@ -538,23 +535,77 @@
     return baseId;
   }
 
-  public RebaseChangeOp getRebaseOp(RevisionResource revRsrc, RebaseInput input, ObjectId baseRev) {
+  public RebaseChangeOp getRebaseOp(
+      RevWalk rw,
+      RevisionResource revRsrc,
+      RebaseInput input,
+      ObjectId baseRev,
+      IdentifiedUser rebaseAsUser)
+      throws ResourceConflictException, PermissionBackendException, IOException {
     return applyRebaseInputToOp(
-        rebaseFactory.create(revRsrc.getNotes(), revRsrc.getPatchSet(), baseRev), input);
+        rw,
+        rebaseFactory.create(revRsrc.getNotes(), revRsrc.getPatchSet(), baseRev),
+        input,
+        rebaseAsUser);
   }
 
   public RebaseChangeOp getRebaseOp(
-      RevisionResource revRsrc, RebaseInput input, Change.Id baseChange) {
+      RevWalk rw,
+      RevisionResource revRsrc,
+      RebaseInput input,
+      Change.Id baseChange,
+      IdentifiedUser rebaseAsUser)
+      throws ResourceConflictException, PermissionBackendException, IOException {
     return applyRebaseInputToOp(
-        rebaseFactory.create(revRsrc.getNotes(), revRsrc.getPatchSet(), baseChange), input);
+        rw,
+        rebaseFactory.create(revRsrc.getNotes(), revRsrc.getPatchSet(), baseChange),
+        input,
+        rebaseAsUser);
   }
 
-  private RebaseChangeOp applyRebaseInputToOp(RebaseChangeOp op, RebaseInput input) {
-    return op.setForceContentMerge(true)
-        .setAllowConflicts(input.allowConflicts)
-        .setMergeStrategy(input.strategy)
-        .setValidationOptions(
-            ValidationOptionsUtil.getValidateOptionsAsMultimap(input.validationOptions))
-        .setFireRevisionCreated(true);
+  private RebaseChangeOp applyRebaseInputToOp(
+      RevWalk rw, RebaseChangeOp op, RebaseInput input, IdentifiedUser rebaseAsUser)
+      throws ResourceConflictException, PermissionBackendException, IOException {
+    RebaseChangeOp rebaseChangeOp =
+        op.setForceContentMerge(true)
+            .setAllowConflicts(input.allowConflicts)
+            .setMergeStrategy(input.strategy)
+            .setValidationOptions(
+                ValidationOptionsUtil.getValidateOptionsAsMultimap(input.validationOptions))
+            .setFireRevisionCreated(true);
+
+    String originalPatchSetCommitterEmail =
+        rw.parseCommit(rebaseChangeOp.getOriginalPatchSet().commitId())
+            .getCommitterIdent()
+            .getEmailAddress();
+
+    if (input.committerEmail != null) {
+      if (!self.get().hasSameAccountId(rebaseAsUser)
+          && !input.committerEmail.equals(rebaseAsUser.getAccount().preferredEmail())
+          && !input.committerEmail.equals(originalPatchSetCommitterEmail)
+          && !permissionBackend.currentUser().test(GlobalPermission.VIEW_SECONDARY_EMAILS)) {
+        throw new ResourceConflictException(
+            String.format(
+                "Cannot rebase using committer email '%s'. It can only be done using the "
+                    + "preferred email or the committer email of the uploader",
+                input.committerEmail));
+      }
+
+      ImmutableSet<String> emails = rebaseAsUser.getEmailAddresses();
+      if (!emails.contains(input.committerEmail)) {
+        throw new ResourceConflictException(
+            String.format(
+                "Cannot rebase using committer email '%s' as it is not a registered "
+                    + "email of the user on whose behalf the rebase operation is performed",
+                input.committerEmail));
+      }
+      rebaseChangeOp.setCommitterIdent(
+          new PersonIdent(
+              rebaseAsUser.getName(),
+              input.committerEmail,
+              TimeUtil.now(),
+              serverIdent.get().getZoneId()));
+    }
+    return rebaseChangeOp;
   }
 }
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index 7dcec60..2ab7e15 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -52,6 +52,10 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Description.Units;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.GpgException;
@@ -70,6 +74,7 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
+import com.google.inject.Singleton;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -91,6 +96,23 @@
     RevisionJson create(Iterable<ListChangesOption> options);
   }
 
+  @Singleton
+  private static class Metrics {
+    private final Timer0 parentDataLatency;
+
+    @Inject
+    Metrics(MetricMaker metricMaker) {
+      parentDataLatency =
+          metricMaker.newTimer(
+              "http/server/rest_api/change_json/to_change_info_latency/parent_data_computation",
+              new Description(
+                      "Latency for computing parent data information in toRevisionInfo"
+                          + " invocations in RevisionJson")
+                  .setCumulative()
+                  .setUnit(Units.MILLISECONDS));
+    }
+  }
+
   private final MergeUtilFactory mergeUtilFactory;
   private final IdentifiedUser.GenericFactory userFactory;
   private final FileInfoJson fileInfoJson;
@@ -109,6 +131,7 @@
   private final GitRepositoryManager repoManager;
   private final PermissionBackend permissionBackend;
   private final ParentDataProvider parentDataProvider;
+  private final Metrics metrics;
 
   @Inject
   RevisionJson(
@@ -129,6 +152,7 @@
       GitRepositoryManager repoManager,
       PermissionBackend permissionBackend,
       ParentDataProvider parentDataProvider,
+      Metrics metrics,
       @Assisted Iterable<ListChangesOption> options) {
     this.userProvider = userProvider;
     this.anonymous = anonymous;
@@ -147,6 +171,7 @@
     this.permissionBackend = permissionBackend;
     this.repoManager = repoManager;
     this.parentDataProvider = parentDataProvider;
+    this.metrics = metrics;
     this.options = ImmutableSet.copyOf(options);
   }
 
@@ -339,14 +364,17 @@
                 c.getId().get());
       }
       if (has(PARENTS)) {
-        String targetBranch =
-            in.branch().isPresent() ? in.branch().get() : cd.change().getDest().branch();
-        List<ParentCommitData> parentData = new ArrayList<>();
-        for (RevCommit parent : commit.getParents()) {
-          ParentCommitData p = parentDataProvider.get(project, repo, parent.getId(), targetBranch);
-          parentData.add(p);
+        try (Timer0.Context ignored = metrics.parentDataLatency.start()) {
+          String targetBranch =
+              in.branch().isPresent() ? in.branch().get() : cd.change().getDest().branch();
+          List<ParentCommitData> parentData = new ArrayList<>();
+          for (RevCommit parent : commit.getParents()) {
+            ParentCommitData p =
+                parentDataProvider.get(project, repo, parent.getId(), targetBranch);
+            parentData.add(p);
+          }
+          out.parentsData = getParentInfo(parentData);
         }
-        out.parentsData = getParentInfo(parentData);
       }
       if (addFooters) {
         Ref ref = repo.exactRef(branchName);
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 7cc843b..254e57b 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -373,20 +373,20 @@
     // bulk new change email.
     Stream<ReviewerInput> inputs =
         Streams.concat(
-            Streams.stream(
-                newReviewerInputFromCommitIdentity(
-                    change,
-                    psInfo.getCommitId(),
-                    psInfo.getAuthor().getAccount(),
-                    NotifyHandling.NONE,
-                    newPatchSet.uploader())),
-            Streams.stream(
-                newReviewerInputFromCommitIdentity(
-                    change,
-                    psInfo.getCommitId(),
-                    psInfo.getCommitter().getAccount(),
-                    NotifyHandling.NONE,
-                    newPatchSet.uploader())));
+            newReviewerInputFromCommitIdentity(
+                change,
+                psInfo.getCommitId(),
+                psInfo.getAuthor().getAccount(),
+                NotifyHandling.NONE,
+                newPatchSet.uploader())
+                .stream(),
+            newReviewerInputFromCommitIdentity(
+                change,
+                psInfo.getCommitId(),
+                psInfo.getCommitter().getAccount(),
+                NotifyHandling.NONE,
+                newPatchSet.uploader())
+                .stream());
     if (magicBranch != null) {
       inputs =
           Streams.concat(
diff --git a/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java b/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java
index 77c284a..6e2f02f 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java
@@ -57,7 +57,7 @@
     StringJoiner footerJoiner = new StringJoiner("\n", "\n\n", "");
     footerJoiner.setEmptyValue("");
     Streams.concat(
-            Streams.stream(getFooterForRename()),
+            getFooterForRename().stream(),
             getFootersForMemberModifications(),
             getFootersForSubgroupModifications())
         .sorted()
diff --git a/java/com/google/gerrit/server/patch/GitPositionTransformer.java b/java/com/google/gerrit/server/patch/GitPositionTransformer.java
index f33d302..b2c02e7 100644
--- a/java/com/google/gerrit/server/patch/GitPositionTransformer.java
+++ b/java/com/google/gerrit/server/patch/GitPositionTransformer.java
@@ -139,8 +139,8 @@
     Set<String> newFiles = newFilesPerOldFile.get(oldFilePath);
     if (newFiles.isEmpty()) {
       // File was deleted.
-      return Streams.stream(
-          positionConflictStrategy.getOnFileConflict(entity.position()).map(entity::withPosition));
+      return positionConflictStrategy.getOnFileConflict(entity.position()).map(entity::withPosition)
+          .stream();
     }
     return newFiles.stream().map(entity::withFilePath);
   }
diff --git a/java/com/google/gerrit/server/project/RefPatternMatcher.java b/java/com/google/gerrit/server/project/RefPatternMatcher.java
index be840b5..798838e 100644
--- a/java/com/google/gerrit/server/project/RefPatternMatcher.java
+++ b/java/com/google/gerrit/server/project/RefPatternMatcher.java
@@ -151,7 +151,7 @@
     }
 
     private ImmutableSet<String> getUsernames(CurrentUser user) {
-      Stream<String> usernames = Streams.stream(user.getUserName());
+      Stream<String> usernames = user.getUserName().stream();
       if (user.isIdentifiedUser()) {
         usernames = Streams.concat(usernames, user.asIdentifiedUser().getEmailAddresses().stream());
       }
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index f8a4a99..51f43ce 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -1165,6 +1165,16 @@
   }
 
   @Operator
+  public Predicate<ChangeData> d(String text) throws QueryParseException {
+    return message(text);
+  }
+
+  @Operator
+  public Predicate<ChangeData> description(String text) throws QueryParseException {
+    return message(text);
+  }
+
+  @Operator
   public Predicate<ChangeData> message(String text) throws QueryParseException {
     if (text.startsWith("^")) {
       checkFieldAvailable(
diff --git a/java/com/google/gerrit/server/restapi/change/Rebase.java b/java/com/google/gerrit/server/restapi/change/Rebase.java
index 167f784..98a3f83 100644
--- a/java/com/google/gerrit/server/restapi/change/Rebase.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebase.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.change.ChangeJson;
 import com.google.gerrit.server.change.ChangeResource;
@@ -68,6 +69,7 @@
   private final ProjectCache projectCache;
   private final PatchSetUtil patchSetUtil;
   private final RebaseMetrics rebaseMetrics;
+  private final IdentifiedUser.GenericFactory userFactory;
 
   @Inject
   public Rebase(
@@ -78,7 +80,8 @@
       PermissionBackend permissionBackend,
       ProjectCache projectCache,
       PatchSetUtil patchSetUtil,
-      RebaseMetrics rebaseMetrics) {
+      RebaseMetrics rebaseMetrics,
+      IdentifiedUser.GenericFactory userFactory) {
     this.updateFactory = updateFactory;
     this.repoManager = repoManager;
     this.rebaseUtil = rebaseUtil;
@@ -87,16 +90,20 @@
     this.projectCache = projectCache;
     this.patchSetUtil = patchSetUtil;
     this.rebaseMetrics = rebaseMetrics;
+    this.userFactory = userFactory;
   }
 
   @Override
   public Response<ChangeInfo> apply(RevisionResource rsrc, RebaseInput input)
       throws UpdateException, RestApiException, IOException, PermissionBackendException {
-
+    IdentifiedUser rebaseAsUser;
     if (input.onBehalfOfUploader && !rsrc.getPatchSet().uploader().equals(rsrc.getAccountId())) {
+      rebaseAsUser =
+          userFactory.runAs(/*remotePeer= */ null, rsrc.getPatchSet().uploader(), rsrc.getUser());
       rsrc.permissions().check(ChangePermission.REBASE_ON_BEHALF_OF_UPLOADER);
-      rsrc = rebaseUtil.onBehalfOf(rsrc, input);
+      rebaseUtil.checkCanRebaseOnBehalfOf(rsrc, input);
     } else {
+      rebaseAsUser = rsrc.getUser().asIdentifiedUser();
       input.onBehalfOfUploader = false;
       rsrc.permissions().check(ChangePermission.REBASE);
     }
@@ -113,14 +120,16 @@
           ObjectReader reader = oi.newReader();
           RevWalk rw = CodeReviewCommit.newRevWalk(reader);
           BatchUpdate bu =
-              updateFactory.create(change.getProject(), rsrc.getUser(), TimeUtil.now())) {
+              updateFactory.create(change.getProject(), rebaseAsUser, TimeUtil.now())) {
         rebaseUtil.verifyRebasePreconditions(rw, rsrc.getNotes(), rsrc.getPatchSet());
 
         RebaseChangeOp rebaseOp =
             rebaseUtil.getRebaseOp(
+                rw,
                 rsrc,
                 input,
-                rebaseUtil.parseOrFindBaseRevision(repo, rw, permissionBackend, rsrc, input, true));
+                rebaseUtil.parseOrFindBaseRevision(repo, rw, permissionBackend, rsrc, input, true),
+                rebaseAsUser);
 
         // TODO(dborowitz): Why no notification? This seems wrong; dig up blame.
         bu.setNotify(NotifyResolver.Result.none());
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChain.java b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
index 343fb72..76c5253 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChain.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
@@ -36,6 +36,7 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.change.ChangeJson;
 import com.google.gerrit.server.change.ChangeResource;
@@ -89,6 +90,7 @@
   private final PatchSetUtil patchSetUtil;
   private final ChangeJson.Factory json;
   private final RebaseMetrics rebaseMetrics;
+  private final IdentifiedUser.GenericFactory userFactory;
 
   @Inject
   RebaseChain(
@@ -103,7 +105,8 @@
       ProjectCache projectCache,
       PatchSetUtil patchSetUtil,
       ChangeJson.Factory json,
-      RebaseMetrics rebaseMetrics) {
+      RebaseMetrics rebaseMetrics,
+      IdentifiedUser.GenericFactory userFactory) {
     this.repoManager = repoManager;
     this.getRelatedChangesUtil = getRelatedChangesUtil;
     this.changeDataFactory = changeDataFactory;
@@ -116,11 +119,18 @@
     this.patchSetUtil = patchSetUtil;
     this.json = json;
     this.rebaseMetrics = rebaseMetrics;
+    this.userFactory = userFactory;
   }
 
   @Override
   public Response<RebaseChainInfo> apply(ChangeResource tipRsrc, RebaseInput input)
       throws IOException, PermissionBackendException, RestApiException, UpdateException {
+    IdentifiedUser rebaseAsUser;
+    if (input.committerEmail != null) {
+      // TODO: committer_email can be supported if all changes in the chain
+      //  belong to the same uploader. It can be attempted in future as needed.
+      throw new BadRequestException("committer_email is not supported when rebasing a chain");
+    }
     if (input.onBehalfOfUploader) {
       tipRsrc.permissions().check(ChangePermission.REBASE_ON_BEHALF_OF_UPLOADER);
       if (input.allowConflicts) {
@@ -160,10 +170,14 @@
               new RevisionResource(changeResourceFactory.create(changeData, user), ps);
           if (input.onBehalfOfUploader
               && !revRsrc.getPatchSet().uploader().equals(revRsrc.getAccountId())) {
-            revRsrc = rebaseUtil.onBehalfOf(revRsrc, input);
+            rebaseAsUser =
+                userFactory.runAs(
+                    /*remotePeer= */ null, revRsrc.getPatchSet().uploader(), revRsrc.getUser());
+            rebaseUtil.checkCanRebaseOnBehalfOf(revRsrc, input);
             revRsrc.permissions().check(ChangePermission.REBASE_ON_BEHALF_OF_UPLOADER);
             anyRebaseOnBehalfOfUploader = true;
           } else {
+            rebaseAsUser = revRsrc.getUser().asIdentifiedUser();
             revRsrc.permissions().check(ChangePermission.REBASE);
           }
           rebaseUtil.verifyRebasePreconditions(rw, changeData.notes(), ps);
@@ -177,7 +191,7 @@
             if (currentBase(rw, ps).equals(desiredBase)) {
               isUpToDate = true;
             } else {
-              rebaseOp = rebaseUtil.getRebaseOp(revRsrc, input, desiredBase);
+              rebaseOp = rebaseUtil.getRebaseOp(rw, revRsrc, input, desiredBase, rebaseAsUser);
             }
           } else {
             if (ancestorsAreUpToDate) {
@@ -187,7 +201,8 @@
               isUpToDate = currentBase(rw, ps).equals(latestCommittedBase);
             }
             if (!isUpToDate) {
-              rebaseOp = rebaseUtil.getRebaseOp(revRsrc, input, chain.get(i - 1).id());
+              rebaseOp =
+                  rebaseUtil.getRebaseOp(rw, revRsrc, input, chain.get(i - 1).id(), rebaseAsUser);
             }
           }
 
@@ -196,7 +211,7 @@
             continue;
           }
           ancestorsAreUpToDate = false;
-          bu.addOp(revRsrc.getChange().getId(), revRsrc.getUser(), rebaseOp);
+          bu.addOp(revRsrc.getChange().getId(), rebaseAsUser, rebaseOp);
           rebaseOps.put(revRsrc.getChange().getId(), rebaseOp);
         }
 
diff --git a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
index 09951b2..e0c699a 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -143,37 +143,41 @@
     if (!changes.isEmpty()) {
       return true;
     }
+    if (commit.getParents() != null && commit.getParents().length > 0) {
+      // Maybe the commit was a merge commit of a change. Try to find promising candidates for
+      // branches to check, by seeing if its parents were associated to changes.
+      // Only request changes from the index if the commit has parents. If size(parents) == 0, then
+      // the query does not make sense (it would request all changes from the project).
+      ImmutableList<Predicate<ChangeData>> parentPredicates =
+          Arrays.stream(commit.getParents())
+              .map(parent -> ChangePredicates.commitPrefix(parent.getId().getName()))
+              .collect(toImmutableList());
+      Predicate<ChangeData> pred =
+          Predicate.and(ChangePredicates.project(project), Predicate.or(parentPredicates));
+      changes =
+          retryHelper
+              .changeIndexQuery(
+                  "queryChangesByProjectCommit", q -> q.enforceVisibility(true).query(pred))
+              .call();
+      Set<Ref> branchesForCommitParents = new HashSet<>(changes.size());
+      for (ChangeData cd : changes) {
+        Ref ref = repo.exactRef(cd.change().getDest().branch());
+        if (ref != null) {
+          branchesForCommitParents.add(ref);
+        }
+      }
 
-    // Maybe the commit was a merge commit of a change. Try to find promising candidates for
-    // branches to check, by seeing if its parents were associated to changes.
-    Predicate<ChangeData> pred =
-        Predicate.and(
-            ChangePredicates.project(project),
-            Predicate.or(
-                Arrays.stream(commit.getParents())
-                    .map(parent -> ChangePredicates.commitPrefix(parent.getId().getName()))
-                    .collect(toImmutableList())));
-    changes =
-        retryHelper
-            .changeIndexQuery(
-                "queryChangesByProjectCommit", q -> q.enforceVisibility(true).query(pred))
-            .call();
-
-    Set<Ref> branchesForCommitParents = new HashSet<>(changes.size());
-    for (ChangeData cd : changes) {
-      Ref ref = repo.exactRef(cd.change().getDest().branch());
-      if (ref != null) {
-        branchesForCommitParents.add(ref);
+      if (reachable.fromRefs(
+          project, repo, commit, branchesForCommitParents.stream().collect(Collectors.toList()))) {
+        return true;
       }
     }
+    // This check covers 2 situations:
+    // 1) The commit does not have any parents. Check if it is visible from any ref in the project.
+    // Exclude change refs, since it is confirmed the commit is not a patchset of any change.
 
-    if (reachable.fromRefs(
-        project, repo, commit, branchesForCommitParents.stream().collect(Collectors.toList()))) {
-      return true;
-    }
-
-    // If we have already checked change refs using the change index, spare any further checks for
-    // changes.
+    // 2) If we have already checked change refs using the change index, spare any further checks
+    // for changes.
     List<Ref> refs =
         repo.getRefDatabase()
             .getRefsByPrefixWithExclusions(RefDatabase.ALL, ImmutableSet.of(RefNames.REFS_CHANGES));
diff --git a/java/com/google/gerrit/server/submit/CommitMergeStatus.java b/java/com/google/gerrit/server/submit/CommitMergeStatus.java
index 4638bfa..f7af684 100644
--- a/java/com/google/gerrit/server/submit/CommitMergeStatus.java
+++ b/java/com/google/gerrit/server/submit/CommitMergeStatus.java
@@ -52,7 +52,7 @@
       "Marking change merged without cherry-picking to branch, as the resulting commit would be"
           + " empty."),
 
-  MISSING_DEPENDENCY("Depends on change that was not submitted."),
+  MISSING_DEPENDENCY("Depends on commit that cannot be merged."),
 
   MANUAL_RECURSIVE_MERGE(
       "The change requires a local merge to resolve.\n"
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 498daae..5af87e8 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -3174,7 +3174,7 @@
 
   @Test
   public void deleteAccount_deletesAccountIdentifiers() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     String secondaryEmail = "secondary@email.com";
     gApi.accounts().id(deleted.id().get()).addEmail(newEmailInput(secondaryEmail));
 
@@ -3219,7 +3219,7 @@
   @Test
   @UseSsh
   public void deleteAccount_deletesSshKeys() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     requestScopeOperations.setApiUser(deleted.id());
     String newKey = TestSshKeys.publicKey(SshSessionFactory.genSshKey(), deleted.email());
     gApi.accounts().self().addSshKey(newKey);
@@ -3237,7 +3237,7 @@
 
   @Test
   public void deleteAccount_deletesGpgKeys() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
 
     requestScopeOperations.setApiUser(deleted.id());
     addExternalIdEmail(
@@ -3262,7 +3262,7 @@
 
   @Test
   public void deleteAccount_deletesStarredChanges() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     PushOneCommit.Result r = createChange();
     String triplet = project.get() + "~master~" + r.getChangeId();
 
@@ -3292,7 +3292,7 @@
 
   @Test
   public void deleteAccount_deletesChangeEdits() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     PushOneCommit.Result r = createChange();
 
     requestScopeOperations.setApiUser(deleted.id());
@@ -3321,7 +3321,7 @@
 
   @Test
   public void deleteAccount_deletesDraftComments() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     PushOneCommit.Result r = createChange();
 
     requestScopeOperations.setApiUser(deleted.id());
@@ -3352,7 +3352,7 @@
   @SuppressWarnings("unused")
   public void deleteAccount_deletesReviewedFlags() throws Exception {
     PushOneCommit.Result r = createChange();
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     ReviewerInput in = new ReviewerInput();
     in.reviewer = deleted.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
@@ -3374,7 +3374,7 @@
 
   @Test
   public void deleteAccount_appliesForSelfById() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     requestScopeOperations.setApiUser(deleted.id());
     gApi.accounts().id(deleted.id().get()).delete();
 
@@ -3384,7 +3384,7 @@
 
   @Test
   public void deleteAccount_throwsForOtherUsers() throws Exception {
-    TestAccount deleted = accountCreator.createValid(testMethodName);
+    TestAccount deleted = accountCreator.createValid(name("deleted"));
     requestScopeOperations.setApiUser(user.id());
     AuthException thrown =
         assertThrows(AuthException.class, () -> gApi.accounts().id(deleted.id().get()).delete());
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index 59ba00b..2379e6a 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -43,7 +43,7 @@
 public class GeneralPreferencesIT extends AbstractDaemonTest {
   @Inject private ExtensionRegistry extensionRegistry;
 
-  private TestAccount user42;
+  protected TestAccount user42;
 
   @Before
   public void setUp() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RebaseChainOnBehalfOfUploaderIT.java b/javatests/com/google/gerrit/acceptance/api/change/RebaseChainOnBehalfOfUploaderIT.java
index 7d1ddfc..297579c 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RebaseChainOnBehalfOfUploaderIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RebaseChainOnBehalfOfUploaderIT.java
@@ -100,6 +100,22 @@
   }
 
   @Test
+  public void cannotRebaseOnBehalfOfUploaderWithCommitterEmail() throws Exception {
+    Account.Id uploader = accountOperations.newAccount().create();
+    Change.Id changeId = changeOperations.newChange().owner(uploader).create();
+    RebaseInput rebaseInput = new RebaseInput();
+    rebaseInput.onBehalfOfUploader = true;
+    rebaseInput.committerEmail = "admin@example.com";
+    BadRequestException exception =
+        assertThrows(
+            BadRequestException.class,
+            () -> gApi.changes().id(changeId.get()).rebaseChain(rebaseInput));
+    assertThat(exception)
+        .hasMessageThat()
+        .isEqualTo("committer_email is not supported when rebasing a chain");
+  }
+
+  @Test
   public void rebaseChangeOnBehalfOfUploader_withRebasePermission() throws Exception {
     testRebaseChainOnBehalfOfUploader(Permission.REBASE);
   }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java b/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java
index d9b079a..c637916 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java
@@ -63,6 +63,7 @@
 import com.google.gerrit.extensions.common.RevisionInfo;
 import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
@@ -144,6 +145,82 @@
     }
 
     @Test
+    public void rebaseWithCommitterEmail() throws Exception {
+      // Create three changes with the same parent
+      PushOneCommit.Result r1 = createChange();
+      testRepo.reset("HEAD~1");
+      PushOneCommit.Result r2 = createChange();
+      testRepo.reset("HEAD~1");
+      PushOneCommit.Result r3 = createChange();
+
+      // Create new user with a secondary email and with permission to rebase
+      Account.Id userWithSecondaryEmail =
+          accountOperations
+              .newAccount()
+              .preferredEmail("preferred@domain.org")
+              .addSecondaryEmail("secondary@domain.org")
+              .create();
+      projectOperations
+          .project(project)
+          .forUpdate()
+          .add(allow(Permission.REBASE).ref("refs/heads/master").group(REGISTERED_USERS))
+          .update();
+
+      // Approve and submit the r1
+      RevisionApi revision = gApi.changes().id(r1.getChangeId()).current();
+      revision.review(ReviewInput.approve());
+      revision.submit();
+
+      // Rebase r2 as the new user with its primary email
+      RebaseInput ri = new RebaseInput();
+      ri.committerEmail = "preferred@domain.org";
+      requestScopeOperations.setApiUser(userWithSecondaryEmail);
+      rebaseCallWithInput.call(r2.getChangeId(), ri);
+      assertThat(r2.getChange().getCommitter().getEmailAddress()).isEqualTo(ri.committerEmail);
+
+      // Approve and submit the r3
+      requestScopeOperations.setApiUser(admin.id());
+      revision = gApi.changes().id(r3.getChangeId()).current();
+      revision.review(ReviewInput.approve());
+      revision.submit();
+
+      // Rebase r2 as the new user with its secondary email
+      ri = new RebaseInput();
+      ri.committerEmail = "secondary@domain.org";
+      requestScopeOperations.setApiUser(userWithSecondaryEmail);
+      rebaseCallWithInput.call(r2.getChangeId(), ri);
+      assertThat(r2.getChange().getCommitter().getEmailAddress()).isEqualTo(ri.committerEmail);
+    }
+
+    @Test
+    public void cannotRebaseWithInvalidCommitterEmail() throws Exception {
+      // Create two changes both with the same parent
+      PushOneCommit.Result c1 = createChange();
+      testRepo.reset("HEAD~1");
+      PushOneCommit.Result c2 = createChange();
+
+      // Approve and submit the first change
+      RevisionApi revision = gApi.changes().id(c1.getChangeId()).current();
+      revision.review(ReviewInput.approve());
+      revision.submit();
+
+      // Rebase the second change with invalid committer email
+      RebaseInput ri = new RebaseInput();
+      ri.committerEmail = "invalid@example.com";
+      ResourceConflictException thrown =
+          assertThrows(
+              ResourceConflictException.class,
+              () -> rebaseCallWithInput.call(c2.getChangeId(), ri));
+      assertThat(thrown)
+          .hasMessageThat()
+          .isEqualTo(
+              String.format(
+                  "Cannot rebase using committer email '%s' as it is not a registered email of "
+                      + "the user on whose behalf the rebase operation is performed",
+                  ri.committerEmail));
+    }
+
+    @Test
     public void rebaseAbandonedChange() throws Exception {
       PushOneCommit.Result r = createChange();
       String changeId = r.getChangeId();
@@ -1096,6 +1173,72 @@
       assertThat(thrown).hasMessageThat().contains("The whole chain is already up to date.");
     }
 
+    @Override
+    @Test
+    public void rebaseWithCommitterEmail() throws Exception {
+      // Create changes with the following hierarchy:
+      // * HEAD
+      //   * r1
+      //   * r2
+
+      PushOneCommit.Result r1 = createChange();
+      testRepo.reset("HEAD~1");
+      PushOneCommit.Result r2 = createChange();
+
+      // Approve and submit the first change
+      RevisionApi revision = gApi.changes().id(r1.getChangeId()).current();
+      revision.review(ReviewInput.approve());
+      revision.submit();
+
+      // Create new user with a secondary email and with permission to rebase
+      Account.Id userWithSecondaryEmail =
+          accountOperations
+              .newAccount()
+              .preferredEmail("preferred@domain.org")
+              .addSecondaryEmail("secondary@domain.org")
+              .create();
+      projectOperations
+          .project(project)
+          .forUpdate()
+          .add(allow(Permission.REBASE).ref("refs/heads/master").group(REGISTERED_USERS))
+          .update();
+
+      // Rebase the chain through r2 with the new user and with its secondary email.
+      RebaseInput ri = new RebaseInput();
+      ri.committerEmail = "secondary@domain.org";
+      requestScopeOperations.setApiUser(userWithSecondaryEmail);
+      BadRequestException exception =
+          assertThrows(
+              BadRequestException.class, () -> gApi.changes().id(r2.getChangeId()).rebaseChain(ri));
+      assertThat(exception)
+          .hasMessageThat()
+          .isEqualTo("committer_email is not supported when rebasing a chain");
+    }
+
+    @Override
+    @Test
+    public void cannotRebaseWithInvalidCommitterEmail() throws Exception {
+      // Create two changes both with the same parent
+      PushOneCommit.Result c1 = createChange();
+      testRepo.reset("HEAD~1");
+      PushOneCommit.Result c2 = createChange();
+
+      // Approve and submit the first change
+      RevisionApi revision = gApi.changes().id(c1.getChangeId()).current();
+      revision.review(ReviewInput.approve());
+      revision.submit();
+
+      // Rebase the second change with invalid committer email
+      RebaseInput ri = new RebaseInput();
+      ri.committerEmail = "invalid@example.com";
+      BadRequestException exception =
+          assertThrows(
+              BadRequestException.class, () -> gApi.changes().id(c2.getChangeId()).rebaseChain(ri));
+      assertThat(exception)
+          .hasMessageThat()
+          .isEqualTo("committer_email is not supported when rebasing a chain");
+    }
+
     @Test
     public void rebaseChain() throws Exception {
       // Create changes with the following hierarchy:
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RebaseOnBehalfOfUploaderIT.java b/javatests/com/google/gerrit/acceptance/api/change/RebaseOnBehalfOfUploaderIT.java
index 968c1f7..319c0cd 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RebaseOnBehalfOfUploaderIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RebaseOnBehalfOfUploaderIT.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth8.assertThat;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
@@ -36,6 +37,7 @@
 import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.Change;
@@ -100,6 +102,103 @@
   }
 
   @Test
+  public void rebaseOnBehalfOfUploaderWithCommitterEmail() throws Exception {
+    allowPermissionToAllUsers(Permission.REBASE);
+
+    String uploaderPreferredEmail = "uploader.preferred@example.com";
+    String uploaderSecondaryEmail = "uploader.secondary@example.com";
+    Account.Id uploader =
+        accountOperations
+            .newAccount()
+            .preferredEmail(uploaderPreferredEmail)
+            .addSecondaryEmail(uploaderSecondaryEmail)
+            .create();
+    Account.Id approver = admin.id();
+    Account.Id rebaser = accountOperations.newAccount().create();
+
+    projectOperations
+        .allProjectsForUpdate()
+        .add(allowCapability(GlobalCapability.VIEW_SECONDARY_EMAILS).group(REGISTERED_USERS))
+        .update();
+
+    // Create two changes both with the same parent.
+    requestScopeOperations.setApiUser(uploader);
+    Change.Id changeToBeTheNewBase =
+        changeOperations.newChange().project(project).owner(uploader).create();
+    Change.Id changeToBeRebased =
+        changeOperations.newChange().project(project).owner(uploader).create();
+
+    // Approve and submit the change that will be the new base for the change that will be rebased.
+    requestScopeOperations.setApiUser(approver);
+    gApi.changes().id(changeToBeTheNewBase.get()).current().review(ReviewInput.approve());
+    gApi.changes().id(changeToBeTheNewBase.get()).current().submit();
+
+    // Rebase the second change on behalf of the uploader
+    requestScopeOperations.setApiUser(rebaser);
+    RebaseInput rebaseInput = new RebaseInput();
+    rebaseInput.onBehalfOfUploader = true;
+    rebaseInput.committerEmail = uploaderSecondaryEmail;
+    gApi.changes().id(changeToBeRebased.get()).rebase(rebaseInput);
+
+    assertThat(
+            gApi.changes()
+                .id(changeToBeRebased.get())
+                .get()
+                .getCurrentRevision()
+                .commit
+                .committer
+                .email)
+        .isEqualTo(uploaderSecondaryEmail);
+  }
+
+  @Test
+  public void cannotRebaseOnBehalfOfUploaderWithCommitterEmailWithoutViewSecondaryEmails()
+      throws Exception {
+    allowPermissionToAllUsers(Permission.REBASE);
+
+    String uploaderPreferredEmail = "uploader.preferred@example.com";
+    String uploaderSecondaryEmail = "uploader.secondary@example.com";
+    Account.Id uploader =
+        accountOperations
+            .newAccount()
+            .preferredEmail(uploaderPreferredEmail)
+            .addSecondaryEmail(uploaderSecondaryEmail)
+            .create();
+    Account.Id approver = admin.id();
+    Account.Id rebaser = accountOperations.newAccount().create();
+
+    // Create two changes both with the same parent.
+    requestScopeOperations.setApiUser(uploader);
+    Change.Id changeToBeTheNewBase =
+        changeOperations.newChange().project(project).owner(uploader).create();
+    Change.Id changeToBeRebased =
+        changeOperations.newChange().project(project).owner(uploader).create();
+
+    // Approve and submit the change that will be the new base for the change that will be rebased.
+    requestScopeOperations.setApiUser(approver);
+    gApi.changes().id(changeToBeTheNewBase.get()).current().review(ReviewInput.approve());
+    gApi.changes().id(changeToBeTheNewBase.get()).current().submit();
+
+    // Rebase the second change on behalf of the uploader
+    requestScopeOperations.setApiUser(rebaser);
+    RebaseInput rebaseInput = new RebaseInput();
+    rebaseInput.onBehalfOfUploader = true;
+    rebaseInput.committerEmail = uploaderSecondaryEmail;
+
+    ResourceConflictException exception =
+        assertThrows(
+            ResourceConflictException.class,
+            () -> gApi.changes().id(changeToBeRebased.get()).rebase(rebaseInput));
+    assertThat(exception)
+        .hasMessageThat()
+        .isEqualTo(
+            String.format(
+                "Cannot rebase using committer email '%s'. It can only be done using "
+                    + "the preferred email or the committer email of the uploader",
+                uploaderSecondaryEmail));
+  }
+
+  @Test
   public void cannotRebaseNonCurrentPatchSetOnBehalfOfUploader() throws Exception {
     Account.Id uploader = accountOperations.newAccount().create();
     Change.Id changeId = changeOperations.newChange().owner(uploader).create();
diff --git a/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java b/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java
index a0ae91b..1143e89 100644
--- a/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java
@@ -427,6 +427,22 @@
     adminRestSession.get("/projects").assertOK();
   }
 
+  @Test
+  public void testNumericChangeIdRedirectWithPrefix() throws Exception {
+    int changeNumber = createChange().getChange().getId().get();
+
+    String redirectUri = String.format("/c/%s/+/%d/", project.get(), changeNumber);
+    anonymousRestSession.get("/c/" + changeNumber).assertTemporaryRedirect(redirectUri);
+  }
+
+  @Test
+  public void testNumericChangeIdRedirectWithoutPrefix() throws Exception {
+    int changeNumber = createChange().getChange().getId().get();
+
+    String redirectUri = String.format("/c/%s/+/%d/", project.get(), changeNumber);
+    anonymousRestSession.get("/" + changeNumber).assertTemporaryRedirect(redirectUri);
+  }
+
   private ObjectId getMetaRefSha1(Result change) {
     return change.getChange().notes().getRevision();
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
index 9c496fa..9d98ecb5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -120,7 +120,7 @@
   }
 
   @Test
-  public void dependencyOnOutdatedPatchSetPreventsMerge() throws Throwable {
+  public void dependencyOnOutdatedPatchSetOfUnsubmittedChangePreventsMerge() throws Throwable {
     // Create a change
     PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
     PushOneCommit.Result changeResult = change.to("refs/for/master");
@@ -147,7 +147,7 @@
         "Failed to submit 2 changes due to the following problems:\n"
             + "Change "
             + change2Result.getChange().getId()
-            + ": Depends on change that was not submitted."
+            + ": Depends on commit that cannot be merged."
             + " Commit "
             + change2Result.getCommit().name()
             + " depends on commit "
@@ -163,4 +163,56 @@
     assertRefUpdatedEvents();
     assertChangeMergedEvents();
   }
+
+  @Test
+  public void dependencyOnOutdatedPatchSetOfSubmittedChangePreventsMerge() throws Throwable {
+    RevCommit initialHead = projectOperations.project(project).getHead("master");
+
+    // Create a change
+    PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
+    PushOneCommit.Result changeResult = change.to("refs/for/master");
+    PatchSet.Id patchSetId = changeResult.getPatchSetId();
+
+    // Create a successor change.
+    PushOneCommit change2 =
+        pushFactory.create(user.newIdent(), testRepo, "feature", "b.txt", "bar");
+    PushOneCommit.Result change2Result = change2.to("refs/for/master");
+
+    // Create new patch set for first change.
+    testRepo.reset(changeResult.getCommit().name());
+    amendChange(changeResult.getChangeId());
+
+    // Approve and submit the first changes
+    approve(changeResult.getChangeId());
+    submit(changeResult.getChangeId());
+    RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
+
+    // Approve the second change
+    approve(change2Result.getChangeId());
+
+    // submit button is disabled.
+    assertSubmitDisabled(change2Result.getChangeId());
+
+    submitWithConflict(
+        change2Result.getChangeId(),
+        "Failed to submit 1 change due to the following problems:\n"
+            + "Change "
+            + change2Result.getChange().getId()
+            + ": Depends on commit that cannot be merged."
+            + " Commit "
+            + change2Result.getCommit().name()
+            + " depends on commit "
+            + changeResult.getCommit().name()
+            + ", which is outdated patch set "
+            + patchSetId.get()
+            + " of change "
+            + changeResult.getChange().getId()
+            + ". The latest patch set is "
+            + changeResult.getPatchSetId().get()
+            + ".");
+
+    // Only events for the first change are sent.
+    assertRefUpdatedEvents(initialHead, headAfterSubmit);
+    assertChangeMergedEvents(changeResult.getChangeId(), headAfterSubmit.name());
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
index aeebc10..a30b5c4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
@@ -417,7 +417,7 @@
         "Failed to submit 2 changes due to the following problems:\n"
             + "Change "
             + change2Result.getChange().getId()
-            + ": Depends on change that was not submitted."
+            + ": Depends on commit that cannot be merged."
             + " Commit "
             + change2Result.getCommit().name()
             + " depends on commit "
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
index 3be49df..a60f757 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -234,7 +234,7 @@
         "Failed to submit 2 changes due to the following problems:\n"
             + "Change "
             + change2Result.getChange().getId()
-            + ": Depends on change that was not submitted."
+            + ": Depends on commit that cannot be merged."
             + " Commit "
             + change2Result.getCommit().name()
             + " depends on commit "
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
index c4f8f2c..ac3622f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
@@ -381,8 +381,7 @@
             + " due to the following problems:\n"
             + "Change "
             + change3a.getChange().getId()
-            + ": Depends on change that"
-            + " was not submitted."
+            + ": Depends on commit that cannot be merged."
             + " Commit "
             + change3a.getCommit().name()
             + " depends on commit "
@@ -485,7 +484,7 @@
         "Failed to submit 1 change due to the following problems:\n"
             + "Change "
             + change3.getPatchSetId().changeId().get()
-            + ": Depends on change that was not submitted."
+            + ": Depends on commit that cannot be merged."
             + " Commit "
             + change3.getCommit().name()
             + " depends on commit "
@@ -518,7 +517,7 @@
         "Failed to submit 1 change due to the following problems:\n"
             + "Change "
             + change2Result.getChange().getId()
-            + ": Depends on change that was not submitted."
+            + ": Depends on commit that cannot be merged."
             + " Commit "
             + change2Result.getCommit().name()
             + " depends on commit "
@@ -584,7 +583,7 @@
         "Failed to submit 1 change due to the following problems:\n"
             + "Change "
             + change2Result.getChange().getId()
-            + ": Depends on change that was not submitted."
+            + ": Depends on commit that cannot be merged."
             + " Commit "
             + change2Result.getCommit().name()
             + " depends on commit "
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
index 71ee90c..4c58521 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
@@ -29,7 +29,7 @@
 public class GetProjectIT extends AbstractDaemonTest {
 
   @Test
-  public void getProject() throws Exception {
+  public void testGetProject() throws Exception {
     String name = project.get();
     ProjectInfo p = gApi.projects().name(name).get();
     assertThat(p.name).isEqualTo(name);
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
index cc1ee00..c06d231 100644
--- a/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
@@ -116,7 +116,7 @@
 
     assertThat(dynamicTemplateData(gerritApi, "/c/project/+/123"))
         .containsAtLeast(
-            "defaultChangeDetailHex", "1916314",
+            "defaultChangeDetailHex", "9916394",
             "changeRequestsPath", "changes/project~123");
   }
 
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 2c012fa..c90f5d4 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -1107,40 +1107,70 @@
   }
 
   @Test
-  public void byMessageExact() throws Exception {
-    repo = createAndOpenProject("repo");
-    RevCommit commit1 = repo.parseBody(repo.commit().message("one").create());
-    Change change1 = insert("repo", newChangeForCommit(repo, commit1));
-    RevCommit commit2 = repo.parseBody(repo.commit().message("two").create());
-    Change change2 = insert("repo", newChangeForCommit(repo, commit2));
-    RevCommit commit3 = repo.parseBody(repo.commit().message("A great \"fix\" to my bug").create());
-    Change change3 = insert("repo", newChangeForCommit(repo, commit3));
-
-    assertQuery("message:foo");
-    assertQuery("message:one", change1);
-    assertQuery("message:two", change2);
-    assertQuery("message:\"great \\\"fix\\\" to\"", change3);
+  public void byMessageExact_byAlias_d() throws Exception {
+    byMessageExact("d:", "d_repo");
   }
 
   @Test
-  public void byMessageRegEx() throws Exception {
+  public void byMessageExact_byAlias_description() throws Exception {
+    byMessageExact("description:", "description_repo");
+  }
+
+  @Test
+  public void byMessageExact_byMainOperator() throws Exception {
+    byMessageExact("message:", "message_repo");
+  }
+
+  private void byMessageExact(String searchOperator, String projectName) throws Exception {
+    repo = createAndOpenProject(projectName);
+    RevCommit commit1 = repo.parseBody(repo.commit().message("one").create());
+    Change change1 = insert(projectName, newChangeForCommit(repo, commit1));
+    RevCommit commit2 = repo.parseBody(repo.commit().message("two").create());
+    Change change2 = insert(projectName, newChangeForCommit(repo, commit2));
+    RevCommit commit3 = repo.parseBody(repo.commit().message("A great \"fix\" to my bug").create());
+    Change change3 = insert(projectName, newChangeForCommit(repo, commit3));
+
+    assertQuery(searchOperator + "foo");
+    assertQuery(searchOperator + "one", change1);
+    assertQuery(searchOperator + "two", change2);
+    assertQuery(searchOperator + "\"great \\\"fix\\\" to\"", change3);
+  }
+
+  @Test
+  public void byMessageRegEx_byAlias_d() throws Exception {
+    byMessageRegEx("d:", "d_repo");
+  }
+
+  @Test
+  public void byMessageRegEx_byAlias_description() throws Exception {
+    byMessageRegEx("description:", "description_repo");
+  }
+
+  @Test
+  public void byMessageRegEx_byMainOperator() throws Exception {
+    byMessageRegEx("message:", "message_repo");
+  }
+
+  private void byMessageRegEx(String searchOperator, String projectName) throws Exception {
     assume().that(getSchema().hasField(ChangeField.COMMIT_MESSAGE_EXACT)).isTrue();
-    repo = createAndOpenProject("repo");
+    repo = createAndOpenProject(projectName);
     RevCommit commit1 = repo.parseBody(repo.commit().message("aaaabcc").create());
-    Change change1 = insert("repo", newChangeForCommit(repo, commit1));
+    Change change1 = insert(projectName, newChangeForCommit(repo, commit1));
     RevCommit commit2 = repo.parseBody(repo.commit().message("aaaacc").create());
-    Change change2 = insert("repo", newChangeForCommit(repo, commit2));
+    Change change2 = insert(projectName, newChangeForCommit(repo, commit2));
     RevCommit commit3 = repo.parseBody(repo.commit().message("Title\n\nHELLO WORLD").create());
-    Change change3 = insert("repo", newChangeForCommit(repo, commit3));
+    Change change3 = insert(projectName, newChangeForCommit(repo, commit3));
     RevCommit commit4 =
         repo.parseBody(repo.commit().message("Title\n\nfoobar hello WORLD").create());
-    Change change4 = insert("repo", newChangeForCommit(repo, commit4));
+    Change change4 = insert(projectName, newChangeForCommit(repo, commit4));
 
-    assertQuery("message:\"^aaaa(b|c)*\"", change2, change1);
-    assertQuery("message:\"^aaaa(c)*c.*\"", change2);
-    assertQuery("message:\"^.*HELLO WORLD.*\"", change3);
+    assertQuery(searchOperator + "\"^aaaa(b|c)*\"", change2, change1);
+    assertQuery(searchOperator + "\"^aaaa(c)*c.*\"", change2);
+    assertQuery(searchOperator + "\"^.*HELLO WORLD.*\"", change3);
     assertQuery(
-        "message:\"^.*(H|h)(E|e)(L|l)(L|l)(O|o) (W|w)(O|o)(R|r)(L|l)(D|d).*\"", change4, change3);
+        searchOperator + "\"^.*(H|h)(E|e)(L|l)(L|l)(O|o) (W|w)(O|o)(R|r)(L|l)(D|d).*\"",
+        change4,
+        change3);
   }
 
   @Test
diff --git a/lib/BUILD b/lib/BUILD
index a4b9d30..f9ece52 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -179,222 +179,10 @@
 )
 
 java_library(
-    name = "flexmark",
+    name = "flexmark-all-lib",
     data = ["//lib:LICENSE-flexmark"],
     visibility = ["//visibility:public"],
-    exports = ["@flexmark//jar"],
-    runtime_deps = [
-        ":flexmark-ext-abbreviation",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-abbreviation",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-abbreviation//jar"],
-    runtime_deps = [
-        ":flexmark-ext-anchorlink",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-anchorlink",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-anchorlink//jar"],
-    runtime_deps = [
-        ":flexmark-ext-autolink",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-autolink",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-autolink//jar"],
-    runtime_deps = [
-        ":flexmark-ext-definition",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-definition",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-definition//jar"],
-    runtime_deps = [
-        ":flexmark-ext-emoji",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-emoji",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-emoji//jar"],
-    runtime_deps = [
-        ":flexmark-ext-escaped-character",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-escaped-character",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-escaped-character//jar"],
-    runtime_deps = [
-        ":flexmark-ext-footnotes",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-footnotes",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-footnotes//jar"],
-    runtime_deps = [
-        ":flexmark-ext-gfm-issues",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-gfm-issues",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-gfm-issues//jar"],
-    runtime_deps = [
-        ":flexmark-ext-gfm-strikethrough",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-gfm-strikethrough",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-gfm-strikethrough//jar"],
-)
-
-java_library(
-    name = "flexmark-ext-gfm-tasklist",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-gfm-tasklist//jar"],
-    runtime_deps = [
-        ":flexmark-ext-gfm-users",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-gfm-users",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-gfm-users//jar"],
-    runtime_deps = [
-        ":flexmark-ext-ins",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-ins",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-ins//jar"],
-    runtime_deps = [
-        ":flexmark-ext-jekyll-front-matter",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-jekyll-front-matter",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-jekyll-front-matter//jar"],
-    runtime_deps = [
-        ":flexmark-ext-superscript",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-superscript",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-superscript//jar"],
-    runtime_deps = [
-        ":flexmark-ext-tables",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-tables",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-tables//jar"],
-    runtime_deps = [
-        ":flexmark-ext-toc",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-toc",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-toc//jar"],
-    runtime_deps = [
-        ":flexmark-ext-typographic",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-typographic",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-typographic//jar"],
-    runtime_deps = [
-        ":flexmark-ext-wikilink",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-wikilink",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-wikilink//jar"],
-    runtime_deps = [
-        ":flexmark-ext-yaml-front-matter",
-    ],
-)
-
-java_library(
-    name = "flexmark-ext-yaml-front-matter",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-ext-yaml-front-matter//jar"],
-)
-
-java_library(
-    name = "flexmark-profile-pegdown",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = ["@flexmark-profile-pegdown//jar"],
-    runtime_deps = [
-        ":flexmark-util",
-    ],
-)
-
-java_library(
-    name = "flexmark-util",
-    data = ["//lib:LICENSE-flexmark"],
-    visibility = ["//visibility:public"],
-    exports = [
-        "@flexmark-util-ast//jar",
-        "@flexmark-util-builder//jar",
-        "@flexmark-util-data//jar",
-        "@flexmark-util-html//jar",
-        "@flexmark-util-misc//jar",
-        "@flexmark-util-sequence//jar",
-        "@flexmark-util-visitor//jar",
-    ],
+    exports = ["@flexmark-all-lib//jar"],
 )
 
 java_library(
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
index 8f835de..31c2b660 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
@@ -1563,6 +1563,7 @@
       base: e.detail.base,
       allow_conflicts: e.detail.allowConflicts,
       on_behalf_of_uploader: e.detail.onBehalfOfUploader,
+      committer_email: e.detail.committerEmail,
     };
     const rebaseChain = !!e.detail.rebaseChain;
     this.fireAction(
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts
index 15500de..b953eec 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.ts
@@ -629,6 +629,7 @@
             allowConflicts: false,
             rebaseChain: false,
             onBehalfOfUploader: true,
+            committerEmail: 'test@default.org',
           },
         })
       );
@@ -636,7 +637,12 @@
         '/rebase',
         assertUIActionInfo(rebaseAction),
         true,
-        {base: '1234', allow_conflicts: false, on_behalf_of_uploader: true},
+        {
+          base: '1234',
+          allow_conflicts: false,
+          on_behalf_of_uploader: true,
+          committer_email: 'test@default.org',
+        },
         {allow_conflicts: false, on_behalf_of_uploader: true},
       ]);
     });
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
index bb76a3e..5e4e255 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
@@ -616,7 +616,6 @@
         topic,
         allow_conflicts: true,
         allow_empty: true,
-        committer_email: this.committerEmail ? this.committerEmail : null,
       };
       const handleError = (response?: Response | null) => {
         this.handleCherryPickFailed(change, response);
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
index 8946a83..7a4caa7 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
@@ -8,18 +8,20 @@
 import {customElement, property, query, state} from 'lit/decorators.js';
 import {when} from 'lit/directives/when.js';
 import {
-  NumericChangeId,
-  BranchName,
-  ChangeActionDialog,
   AccountDetailInfo,
   AccountInfo,
+  BranchName,
+  ChangeActionDialog,
+  EmailInfo,
+  NumericChangeId,
+  GitPersonInfo,
 } from '../../../types/common';
 import '../../shared/gr-dialog/gr-dialog';
 import '../../shared/gr-autocomplete/gr-autocomplete';
 import {
-  GrAutocomplete,
   AutocompleteQuery,
   AutocompleteSuggestion,
+  GrAutocomplete,
 } from '../../shared/gr-autocomplete/gr-autocomplete';
 import {getAppContext} from '../../../services/app-context';
 import {sharedStyles} from '../../../styles/shared-styles';
@@ -43,6 +45,7 @@
   allowConflicts: boolean;
   rebaseChain: boolean;
   onBehalfOfUploader: boolean;
+  committerEmail: string | null;
 }
 
 @customElement('gr-confirm-rebase-dialog')
@@ -92,6 +95,18 @@
   @state()
   allowConflicts = false;
 
+  @state()
+  selectedEmailForRebase: string | null | undefined;
+
+  @state()
+  currentUserEmails: EmailInfo[] = [];
+
+  @state()
+  uploaderEmails: EmailInfo[] = [];
+
+  @state()
+  committerEmailDropdownItems: EmailInfo[] = [];
+
   @query('#rebaseOnParentInput')
   private rebaseOnParentInput?: HTMLInputElement;
 
@@ -116,6 +131,9 @@
   @state()
   uploader?: AccountInfo;
 
+  @state()
+  latestCommitter?: GitPersonInfo;
+
   private readonly restApiService = getAppContext().restApiService;
 
   private readonly getChangeModel = resolve(this, changeModelToken);
@@ -150,6 +168,16 @@
       () => this.getRelatedChangesModel().hasParent$,
       x => (this.hasParent = x)
     );
+    subscribe(
+      this,
+      () => this.getChangeModel().latestCommitter$,
+      x => (this.latestCommitter = x)
+    );
+  }
+
+  override connectedCallback() {
+    super.connectedCallback();
+    this.loadCommitterEmailDropdownItems();
   }
 
   override willUpdate(changedProperties: PropertyValues): void {
@@ -194,6 +222,9 @@
         .rebaseOnBehalfMsg {
           margin-top: var(--spacing-m);
         }
+        .rebaseWithCommitterEmail {
+          margin-top: var(--spacing-m);
+        }
       `,
     ];
   }
@@ -288,6 +319,7 @@
               type="checkbox"
               @change=${() => {
                 this.allowConflicts = !!this.rebaseAllowConflicts?.checked;
+                this.loadCommitterEmailDropdownItems();
               }}
             />
             <label for="rebaseAllowConflicts"
@@ -311,6 +343,9 @@
                   type="checkbox"
                   @change=${() => {
                     this.shouldRebaseChain = !!this.rebaseChain?.checked;
+                    if (this.shouldRebaseChain) {
+                      this.selectedEmailForRebase = undefined;
+                    }
                   }}
                 />
                 <label for="rebaseChain">Rebase all ancestors</label>
@@ -325,6 +360,18 @@
               ></gr-account-chip
               ><span></div>`
           )}
+          ${when(
+            this.canShowCommitterEmailDropdown(),
+            () => html`<div class="rebaseWithCommitterEmail"
+            >Rebase with committer email
+                <gr-dropdown-list
+                    .items=${this.getCommitterEmailDropdownItems()}
+                    .value=${this.selectedEmailForRebase}
+                    @value-change=${this.handleCommitterEmailDropdownItems}
+                >
+                </gr-dropdown-list>
+                <span></div>`
+          )}
         </div>
       </gr-dialog>
     `;
@@ -377,6 +424,69 @@
     );
   }
 
+  private setPreferredAsSelectedEmailForRebase(emails: EmailInfo[]) {
+    emails.forEach(e => {
+      if (e.preferred) {
+        this.selectedEmailForRebase = e.email;
+      }
+    });
+  }
+
+  private canShowCommitterEmailDropdown() {
+    return (
+      this.committerEmailDropdownItems &&
+      this.committerEmailDropdownItems.length > 1 &&
+      !this.shouldRebaseChain
+    );
+  }
+
+  private getCommitterEmailDropdownItems() {
+    return this.committerEmailDropdownItems?.map(e => {
+      return {
+        text: e.email,
+        value: e.email,
+      };
+    });
+  }
+
+  private isLatestCommitterEmailInDropdownItems(): boolean {
+    return this.committerEmailDropdownItems?.some(
+      e => e.email === this.latestCommitter?.email.toString()
+    );
+  }
+
+  public setSelectedEmailForRebase() {
+    if (this.isLatestCommitterEmailInDropdownItems()) {
+      this.selectedEmailForRebase = this.latestCommitter?.email;
+    } else {
+      this.setPreferredAsSelectedEmailForRebase(
+        this.committerEmailDropdownItems
+      );
+    }
+  }
+
+  async loadCommitterEmailDropdownItems() {
+    if (this.isCurrentUserEqualToLatestUploader() || this.allowConflicts) {
+      const currentUserEmails = await this.restApiService.getAccountEmails();
+      this.committerEmailDropdownItems = currentUserEmails || [];
+    } else if (this.uploader && this.uploader.email) {
+      const currentUploaderEmails =
+        await this.restApiService.getAccountEmailsFor(
+          this.uploader.email.toString()
+        );
+      this.committerEmailDropdownItems = currentUploaderEmails || [];
+    } else {
+      this.committerEmailDropdownItems = [];
+    }
+    if (this.committerEmailDropdownItems) {
+      this.setSelectedEmailForRebase();
+    }
+  }
+
+  private handleCommitterEmailDropdownItems(e: CustomEvent<{value: string}>) {
+    this.selectedEmailForRebase = e.detail.value;
+  }
+
   filterChanges(
     input: string,
     changes: RebaseChange[]
@@ -436,6 +546,7 @@
       allowConflicts: !!this.rebaseAllowConflicts?.checked,
       rebaseChain: !!this.rebaseChain?.checked,
       onBehalfOfUploader: this.rebaseOnBehalfOfUploader(),
+      committerEmail: this.selectedEmailForRebase || null,
     };
     fireNoBubbleNoCompose(this, 'confirm-rebase', detail);
     this.text = '';
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts
index f43e521..e6326b3 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts
@@ -8,11 +8,18 @@
 import {GrConfirmRebaseDialog, RebaseChange} from './gr-confirm-rebase-dialog';
 import {
   pressKey,
+  query,
   queryAndAssert,
   stubRestApi,
   waitUntil,
 } from '../../../test/test-utils';
-import {NumericChangeId, BranchName, Timestamp} from '../../../types/common';
+import {
+  NumericChangeId,
+  BranchName,
+  Timestamp,
+  AccountId,
+  EmailAddress,
+} from '../../../types/common';
 import {
   createAccountWithEmail,
   createChangeViewChange,
@@ -25,6 +32,7 @@
 import {changeModelToken} from '../../../models/change/change-model';
 import {GrAccountChip} from '../../shared/gr-account-chip/gr-account-chip';
 import {LoadingStatus} from '../../../types/types';
+import {GrDropdownList} from '../../shared/gr-dropdown-list/gr-dropdown-list';
 
 suite('gr-confirm-rebase-dialog tests', () => {
   let element: GrConfirmRebaseDialog;
@@ -157,6 +165,131 @@
     });
   });
 
+  suite('rebase with committer email', () => {
+    setup(async () => {
+      element.branch = 'test' as BranchName;
+      await element.updateComplete;
+    });
+
+    test('hide rebaseWithCommitterEmail dialog when committer has single email', async () => {
+      element.committerEmailDropdownItems = [
+        {
+          email: 'test1@example.com',
+          preferred: true,
+          pending_confirmation: true,
+        },
+      ];
+      await element.updateComplete;
+      assert.isNotOk(query(element, '.rebaseWithCommitterEmail'));
+    });
+
+    test('show rebaseWithCommitterEmail dialog when committer has more than one email', async () => {
+      element.committerEmailDropdownItems = [
+        {
+          email: 'test1@example.com',
+          preferred: true,
+          pending_confirmation: true,
+        },
+        {
+          email: 'test2@example.com',
+          pending_confirmation: true,
+        },
+      ];
+      await element.updateComplete;
+      const committerEmail = queryAndAssert(
+        element,
+        '.rebaseWithCommitterEmail'
+      );
+      assert.dom.equal(
+        committerEmail,
+        /* HTML */ `<div class="rebaseWithCommitterEmail"
+              >Rebase with committer email
+              <gr-dropdown-list>
+              </gr-dropdown-list>
+              <span></div>`
+      );
+      const dropdownList: GrDropdownList = queryAndAssert(
+        committerEmail,
+        'gr-dropdown-list'
+      );
+      assert.strictEqual(dropdownList.items!.length, 2);
+    });
+
+    test('hide rebaseWithCommitterEmail dialog when RebaseChain is set', async () => {
+      element.shouldRebaseChain = true;
+      await element.updateComplete;
+      assert.isNotOk(query(element, '.rebaseWithCommitterEmail'));
+    });
+
+    test('show current user emails in the dropdown list when rebase with conflicts is allowed', async () => {
+      element.allowConflicts = true;
+      element.latestCommitter = {
+        email: 'commit@example.com' as EmailAddress,
+        name: 'committer',
+        date: '2023-06-12 18:32:08.000000000' as Timestamp,
+      };
+      element.committerEmailDropdownItems = [
+        {
+          email: 'currentuser1@example.com',
+          preferred: true,
+          pending_confirmation: true,
+        },
+        {
+          email: 'currentuser2@example.com',
+          pending_confirmation: true,
+        },
+      ];
+      await element.updateComplete;
+      const committerEmail = queryAndAssert(
+        element,
+        '.rebaseWithCommitterEmail'
+      );
+      const dropdownList: GrDropdownList = queryAndAssert(
+        committerEmail,
+        'gr-dropdown-list'
+      );
+      assert.deepStrictEqual(
+        dropdownList.items!.map(e => e.value),
+        element.committerEmailDropdownItems.map(e => e.email)
+      );
+    });
+
+    test('show uploader emails in the dropdown list when rebase with conflicts is not allowed', async () => {
+      element.allowConflicts = false;
+      element.uploader = {_account_id: 2 as AccountId, name: '2'};
+      element.latestCommitter = {
+        email: 'commit@example.com' as EmailAddress,
+        name: 'committer',
+        date: '2023-06-12 18:32:08.000000000' as Timestamp,
+      };
+      element.committerEmailDropdownItems = [
+        {
+          email: 'uploader1@example.com',
+          preferred: true,
+          pending_confirmation: true,
+        },
+        {
+          email: 'uploader2@example.com',
+          preferred: false,
+          pending_confirmation: true,
+        },
+      ];
+      await element.updateComplete;
+      const committerEmail = queryAndAssert(
+        element,
+        '.rebaseWithCommitterEmail'
+      );
+      const dropdownList: GrDropdownList = queryAndAssert(
+        committerEmail,
+        'gr-dropdown-list'
+      );
+      assert.deepStrictEqual(
+        dropdownList.items!.map(e => e.value),
+        element.committerEmailDropdownItems.map(e => e.email)
+      );
+    });
+  });
+
   test('disableActions property disables dialog confirm', async () => {
     element.disableActions = false;
     await element.updateComplete;
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents.ts b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents.ts
index ac6fa57..ffc588e 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents.ts
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents.ts
@@ -105,6 +105,7 @@
         .top,
         .attention,
         .status,
+        .displayName,
         .voteable {
           padding: var(--spacing-s) var(--spacing-l);
         }
@@ -181,7 +182,8 @@
         </div>
       </div>
       ${this.renderAccountStatusPlugins()} ${this.renderAccountStatus()}
-      ${this.renderLinks()} ${this.renderChangeRelatedInfoAndActions()}
+      ${this.renderDisplayName()} ${this.renderLinks()}
+      ${this.renderChangeRelatedInfoAndActions()}
     `;
   }
 
@@ -281,6 +283,16 @@
     `;
   }
 
+  private renderDisplayName() {
+    if (!this.account.display_name) return nothing;
+    return html`
+      <div class="displayName">
+        <span class="title">Display name:</span>
+        <span class="value">${this.account.display_name.trim()}</span>
+      </div>
+    `;
+  }
+
   private renderNeedsAttention() {
     if (!(this.isAttentionEnabled && this.hasUserAttention)) return nothing;
     const lastUpdate = getLastUpdate(this.account, this.change);
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents_test.ts b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents_test.ts
index c3c48cb..281d295 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account-contents_test.ts
@@ -35,6 +35,7 @@
   const ACCOUNT: AccountDetailInfo = {
     ...createAccountDetailWithId(31),
     email: 'kermit@gmail.com' as EmailAddress,
+    display_name: 'Just Kermit',
     username: 'kermit',
     name: 'Kermit The Frog',
     status: '  I am a frog  ',
@@ -76,6 +77,10 @@
           <span class="title">About me:</span>
           <span class="value">I am a frog</span>
         </div>
+        <div class="displayName">
+          <span class="title">Display name:</span>
+          <span class="value">Just Kermit</span>
+        </div>
         <div class="links">
           <gr-icon icon="link" class="linkIcon"></gr-icon>
           <a href="/q/owner:kermit@gmail.com">Changes</a>
@@ -111,6 +116,10 @@
           <span class="title"> About me: </span>
           <span class="value"> I am a frog </span>
         </div>
+        <div class="displayName">
+          <span class="title">Display name:</span>
+          <span class="value">Just Kermit</span>
+        </div>
         <div class="links">
           <gr-icon class="linkIcon" icon="link"> </gr-icon>
           <a href="/q/owner:kermit@gmail.com"> Changes </a>
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
index 3696f99..58cad19 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
@@ -431,7 +431,6 @@
   }
 
   override render() {
-    fire(this, 'render-start', {});
     return html`<gr-diff-element></gr-diff-element>`;
   }
 
diff --git a/polygerrit-ui/app/services/app-context-init.ts b/polygerrit-ui/app/services/app-context-init.ts
index 87972de..40517ac 100644
--- a/polygerrit-ui/app/services/app-context-init.ts
+++ b/polygerrit-ui/app/services/app-context-init.ts
@@ -88,7 +88,8 @@
     authService: (_ctx: Partial<AppContext>) => new Auth(),
     restApiService: (ctx: Partial<AppContext>) => {
       assertIsDefined(ctx.authService, 'authService');
-      return new GrRestApiServiceImpl(ctx.authService);
+      assertIsDefined(ctx.flagsService, 'flagsService');
+      return new GrRestApiServiceImpl(ctx.authService, ctx.flagsService);
     },
   };
   return create<AppContext>(appRegistry);
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
index 68d13d9..5764ea8 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
@@ -148,6 +148,7 @@
 import {BaseScheduler, Scheduler} from '../scheduler/scheduler';
 import {MaxInFlightScheduler} from '../scheduler/max-in-flight-scheduler';
 import {escapeAndWrapSearchOperatorValue} from '../../utils/string-util';
+import {FlagsService, KnownExperimentId} from '../flags/flags';
 
 const MAX_PROJECT_RESULTS = 25;
 export const PROBE_PATH = '/Documentation/index.html';
@@ -221,7 +222,7 @@
 }
 
 interface QueryChangesParams {
-  [paramName: string]: string | undefined | number | string[] | boolean;
+  [paramName: string]: string | undefined | number | string[];
   O?: string; // options
   S: number; // start
   n?: number; // changes per page
@@ -301,7 +302,10 @@
   // Used to serialize requests for certain RPCs
   readonly _serialScheduler: Scheduler<Response>;
 
-  constructor(private readonly authService: AuthService) {
+  constructor(
+    private readonly authService: AuthService,
+    private readonly flagService: FlagsService
+  ) {
     this._restApiHelper = new GrRestApiHelper(
       this._cache,
       this.authService,
@@ -803,6 +807,26 @@
     } else return;
   }
 
+  getAccountEmailsFor(email: string) {
+    return this.getLoggedIn()
+      .then(isLoggedIn => {
+        if (isLoggedIn) {
+          return this.getAccountCapabilities();
+        } else {
+          return undefined;
+        }
+      })
+      .then((capabilities: AccountCapabilityInfo | undefined) => {
+        if (capabilities && capabilities.viewSecondaryEmails) {
+          return this._fetchSharedCacheURL({
+            url: '/accounts/' + email + '/emails',
+            reportUrlAsIs: true,
+          }) as Promise<EmailInfo[] | undefined>;
+        }
+        return undefined;
+      });
+  }
+
   addAccountEmail(email: string): Promise<Response> {
     return this._restApiHelper.send({
       method: HttpMethod.PUT,
@@ -1058,7 +1082,6 @@
     if (query && query.length > 0) {
       params.q = query;
     }
-    params['allow-incomplete-results'] = true;
     const request = {
       url: '/changes/',
       params,
@@ -1230,11 +1253,8 @@
   }
 
   async getChangeOptionsHex(): Promise<string> {
-    if (
-      window.DEFAULT_DETAIL_HEXES &&
-      window.DEFAULT_DETAIL_HEXES.dashboardPage
-    ) {
-      return window.DEFAULT_DETAIL_HEXES.dashboardPage;
+    if (window.DEFAULT_DETAIL_HEXES && window.DEFAULT_DETAIL_HEXES.changePage) {
+      return window.DEFAULT_DETAIL_HEXES.changePage;
     }
     return listChangesOptionsToHex(...(await this.getChangeOptions()));
   }
@@ -1243,7 +1263,7 @@
     const config = await this.getConfig(false);
 
     // This list MUST be kept in sync with
-    // ChangeIT#changeDetailsDoesNotRequireIndex
+    // ChangeIT#changeDetailsDoesNotRequireIndex and IndexPreloadingUtil#CHANGE_DETAIL_OPTIONS
     // This list MUST be kept in sync with getResponseFormatOptions
     const options = [
       ListChangesOption.ALL_COMMITS,
@@ -1257,8 +1277,10 @@
       ListChangesOption.WEB_LINKS,
       ListChangesOption.SKIP_DIFFSTAT,
       ListChangesOption.SUBMIT_REQUIREMENTS,
-      ListChangesOption.PARENTS,
     ];
+    if (this.flagService.isEnabled(KnownExperimentId.REVISION_PARENTS_DATA)) {
+      options.push(ListChangesOption.PARENTS);
+    }
     if (config?.receive?.enable_signed_push) {
       options.push(ListChangesOption.PUSH_CERTIFICATES);
     }
@@ -1269,7 +1291,7 @@
     const config = await this.getConfig(false);
 
     // This list MUST be kept in sync with
-    // ChangeIT#changeDetailsDoesNotRequireIndex
+    // ChangeIT#changeDetailsDoesNotRequireIndex and IndexPreloadingUtil#CHANGE_DETAIL_OPTIONS
     // This list MUST be kept in sync with getChangeOptions
     const options = [
       'ALL_COMMITS',
@@ -1283,8 +1305,10 @@
       'WEB_LINKS',
       'SKIP_DIFFSTAT',
       'SUBMIT_REQUIREMENTS',
-      'PARENTS',
     ];
+    if (this.flagService.isEnabled(KnownExperimentId.REVISION_PARENTS_DATA)) {
+      options.push('PARENTS');
+    }
     if (config?.receive?.enable_signed_push) {
       options.push('PUSH_CERTIFICATES');
     }
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl_test.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl_test.ts
index 2fd7ba1..9e5967f 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl_test.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl_test.ts
@@ -62,6 +62,7 @@
 import {AuthService} from '../gr-auth/gr-auth';
 import {GrAuthMock} from '../gr-auth/gr-auth_mock';
 import {getBaseUrl} from '../../utils/url-util';
+import {FlagsServiceImplementation} from '../flags/flags_impl';
 
 const EXPECTED_QUERY_OPTIONS = listChangesOptionsToHex(
   ListChangesOption.CHANGE_ACTIONS,
@@ -91,7 +92,10 @@
     // fake auth
     authService = new GrAuthMock();
     sinon.stub(authService, 'authCheck').resolves(true);
-    element = new GrRestApiServiceImpl(authService);
+    element = new GrRestApiServiceImpl(
+      authService,
+      new FlagsServiceImplementation()
+    );
 
     element._projectLookup = {};
   });
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts
index b081ff8..c70f780 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts
@@ -229,6 +229,7 @@
   saveEditPreferences(prefs: EditPreferencesInfo): Promise<Response>;
 
   getAccountEmails(): Promise<EmailInfo[] | undefined>;
+  getAccountEmailsFor(email: string): Promise<EmailInfo[] | undefined>;
   deleteAccountEmail(email: string): Promise<Response>;
   setPreferredAccountEmail(email: string): Promise<void>;
 
diff --git a/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts b/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
index b6fef81..e0a1682 100644
--- a/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
+++ b/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
@@ -191,6 +191,9 @@
   getAccountEmails(): Promise<EmailInfo[] | undefined> {
     return Promise.resolve([]);
   },
+  getAccountEmailsFor(): Promise<EmailInfo[] | undefined> {
+    return Promise.resolve([]);
+  },
   getAccountGPGKeys(): Promise<Record<string, GpgKeyInfo>> {
     return Promise.resolve({});
   },
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index 7f197d6..8396abc 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -1292,6 +1292,7 @@
   viewConnections?: boolean;
   viewPlugins?: boolean;
   viewQueue?: boolean;
+  viewSecondaryEmails?: boolean;
 }
 
 /**
diff --git a/tools/deps.bzl b/tools/deps.bzl
index dfd49ce..f6ee1d8 100644
--- a/tools/deps.bzl
+++ b/tools/deps.bzl
@@ -3,7 +3,6 @@
 CAFFEINE_VERS = "2.9.2"
 ANTLR_VERS = "3.5.2"
 COMMONMARK_VERSION = "0.21.0"
-FLEXMARK_VERS = "0.64.0"
 GREENMAIL_VERS = "1.5.5"
 MAIL_VERS = "1.6.0"
 MIME4J_VERS = "0.8.1"
@@ -203,178 +202,10 @@
     )
 
     maven_jar(
-        name = "flexmark",
-        artifact = "com.vladsch.flexmark:flexmark:" + FLEXMARK_VERS,
-        sha1 = "bb5fcdf1335a35c4c0285fee2683a32e6a70cd59",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-abbreviation",
-        artifact = "com.vladsch.flexmark:flexmark-ext-abbreviation:" + FLEXMARK_VERS,
-        sha1 = "48a64d5d1e5d1e0f8efde75f051b16800952eeb3",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-anchorlink",
-        artifact = "com.vladsch.flexmark:flexmark-ext-anchorlink:" + FLEXMARK_VERS,
-        sha1 = "e263a138896153bad6b69828d65c409fd99292a8",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-autolink",
-        artifact = "com.vladsch.flexmark:flexmark-ext-autolink:" + FLEXMARK_VERS,
-        sha1 = "e380ee48d5a779d08f071aaaf9a69e5985f24802",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-definition",
-        artifact = "com.vladsch.flexmark:flexmark-ext-definition:" + FLEXMARK_VERS,
-        sha1 = "892d83daafb1255489377a4331c6b04cd9fef288",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-emoji",
-        artifact = "com.vladsch.flexmark:flexmark-ext-emoji:" + FLEXMARK_VERS,
-        sha1 = "63777b6b82456fb08587537db949c67ac6ac5d66",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-escaped-character",
-        artifact = "com.vladsch.flexmark:flexmark-ext-escaped-character:" + FLEXMARK_VERS,
-        sha1 = "19b48848dd0d242c8dd527113cf91711847cf7f1",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-footnotes",
-        artifact = "com.vladsch.flexmark:flexmark-ext-footnotes:" + FLEXMARK_VERS,
-        sha1 = "a885e4c512f6f73b3ea02a26f71e0116e720b33e",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-gfm-issues",
-        artifact = "com.vladsch.flexmark:flexmark-ext-gfm-issues:" + FLEXMARK_VERS,
-        sha1 = "f1f2e840cd295bfaad006e7bddb715edfd747e44",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-gfm-strikethrough",
-        artifact = "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:" + FLEXMARK_VERS,
-        sha1 = "9e4d01143752ebe98d45ac29c77e17fa8cc8b947",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-gfm-tasklist",
-        artifact = "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:" + FLEXMARK_VERS,
-        sha1 = "ad5d3dda8a17100ee01606386aacb73d41e35764",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-gfm-users",
-        artifact = "com.vladsch.flexmark:flexmark-ext-gfm-users:" + FLEXMARK_VERS,
-        sha1 = "59846560518f6ef5282d333e1ba1a68dc5ea8c43",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-ins",
-        artifact = "com.vladsch.flexmark:flexmark-ext-ins:" + FLEXMARK_VERS,
-        sha1 = "b5a7e72e337d31e2956f662b37ec1f47289e7751",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-jekyll-front-matter",
-        artifact = "com.vladsch.flexmark:flexmark-ext-jekyll-front-matter:" + FLEXMARK_VERS,
-        sha1 = "5543c826f325444edacc6920992753568aa046f6",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-superscript",
-        artifact = "com.vladsch.flexmark:flexmark-ext-superscript:" + FLEXMARK_VERS,
-        sha1 = "bcee70d603fb7acf9ef490036b14a5c1cb3f533c",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-tables",
-        artifact = "com.vladsch.flexmark:flexmark-ext-tables:" + FLEXMARK_VERS,
-        sha1 = "52b829753bb928cd3f26c43d39639eb95b3f9f88",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-toc",
-        artifact = "com.vladsch.flexmark:flexmark-ext-toc:" + FLEXMARK_VERS,
-        sha1 = "69b96da85758688a564babea371fc9268a11d718",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-typographic",
-        artifact = "com.vladsch.flexmark:flexmark-ext-typographic:" + FLEXMARK_VERS,
-        sha1 = "5e2cb29da6c43eecffbd34a4f69976f61a383e97",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-wikilink",
-        artifact = "com.vladsch.flexmark:flexmark-ext-wikilink:" + FLEXMARK_VERS,
-        sha1 = "5ada81fecf0d68cdcd5c763da0f5f22aa885d559",
-    )
-
-    maven_jar(
-        name = "flexmark-ext-yaml-front-matter",
-        artifact = "com.vladsch.flexmark:flexmark-ext-yaml-front-matter:" + FLEXMARK_VERS,
-        sha1 = "377b29278cad7603aa3ca705c31a9f48cb6f8fb9",
-    )
-
-    maven_jar(
-        name = "flexmark-profile-pegdown",
-        artifact = "com.vladsch.flexmark:flexmark-profile-pegdown:" + FLEXMARK_VERS,
-        sha1 = "c67446ffd3653416ed26f924ba99f8125fb74791",
-    )
-
-    maven_jar(
-        name = "flexmark-util-data",
-        artifact = "com.vladsch.flexmark:flexmark-util-data:" + FLEXMARK_VERS,
+        name = "flexmark-all-lib",
+        artifact = "com.vladsch.flexmark:flexmark-all:0.64.0:lib",
         attach_source = False,
-        sha1 = "63162607faa7c98f6e50a9d1e826004c5475841d",
-    )
-
-    maven_jar(
-        name = "flexmark-util-ast",
-        artifact = "com.vladsch.flexmark:flexmark-util-ast:" + FLEXMARK_VERS,
-        attach_source = False,
-        sha1 = "10fff87e61b7c3bb12afddf7b418977cd02acdc8",
-    )
-
-    maven_jar(
-        name = "flexmark-util-misc",
-        artifact = "com.vladsch.flexmark:flexmark-util-misc:" + FLEXMARK_VERS,
-        attach_source = False,
-        sha1 = "d2129f4f2b55fbf645e3499c7b0cdddcfef81112",
-    )
-
-    maven_jar(
-        name = "flexmark-util-visitor",
-        artifact = "com.vladsch.flexmark:flexmark-util-visitor:" + FLEXMARK_VERS,
-        attach_source = False,
-        sha1 = "7eb0030ae2a2eec80d74790caeb4cdb6b4bf8e17",
-    )
-
-    maven_jar(
-        name = "flexmark-util-builder",
-        artifact = "com.vladsch.flexmark:flexmark-util-builder:" + FLEXMARK_VERS,
-        attach_source = False,
-        sha1 = "2d6adaf6053f72de86a050fe6967049f7c2d3500",
-    )
-
-    maven_jar(
-        name = "flexmark-util-sequence",
-        artifact = "com.vladsch.flexmark:flexmark-util-sequence:" + FLEXMARK_VERS,
-        attach_source = False,
-        sha1 = "299e3b4a4272ba9dcd6b9081b8d24d1a672a0921",
-    )
-
-    maven_jar(
-        name = "flexmark-util-html",
-        artifact = "com.vladsch.flexmark:flexmark-util-html:" + FLEXMARK_VERS,
-        attach_source = False,
-        sha1 = "fc52f860cf45f57468d8f14ee63c1e6469ee3f47",
+        sha1 = "de92cef20c1f61681a3c8f64dd5975fbd3125049",
     )
 
     # Transitive dependency of flexmark and gitiles