Rebase:chain - skip merged ancestors

Bug: Google b/279914754
Release-Notes: skip
Change-Id: Iaaefef7901b84e10faf7b38b80bdc227866edc07
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChain.java b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
index b8afcb7..343fb72 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChain.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
@@ -317,10 +317,18 @@
 
   private List<PatchSetData> getChainForCurrentPatchSet(ChangeResource rsrc)
       throws PermissionBackendException, IOException {
-    return Lists.reverse(
-        getRelatedChangesUtil.getAncestors(
-            changeDataFactory.create(rsrc.getNotes()),
-            patchSetUtil.current(rsrc.getNotes()),
-            true));
+    List<PatchSetData> ancestors =
+        Lists.reverse(
+            getRelatedChangesUtil.getAncestors(
+                changeDataFactory.create(rsrc.getNotes()),
+                patchSetUtil.current(rsrc.getNotes()),
+                true));
+    int eldestOpenAncestor = 0;
+    for (PatchSetData ps : ancestors) {
+      if (ps.data().change().isMerged()) {
+        eldestOpenAncestor++;
+      }
+    }
+    return ancestors.subList(eldestOpenAncestor, ancestors.size());
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java b/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java
index fd37fb0..5ecb5a7 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RebaseIT.java
@@ -986,7 +986,7 @@
     public void rebaseChain() throws Exception {
       // Create changes with the following hierarchy:
       // * HEAD
-      //   * r1
+      //   * r (merged)
       //   * r2
       //     * r3
       //       * r4
@@ -1043,7 +1043,7 @@
       final String newContent = "new content";
       // Create changes with the following revision hierarchy:
       // * HEAD
-      //   * r1
+      //   * r (merged)
       //   * r2
       //     * r3/1    r3/2
       //       * r4
@@ -1079,6 +1079,71 @@
     }
 
     @Test
+    public void rebaseChainWithMergedAncestor() throws Exception {
+      final String file = "modified_file.txt";
+      final String newContent = "new content";
+
+      // Create changes with the following hierarchy:
+      // * HEAD
+      //   * r (merged)
+      //   * r2.1         r2.2 (merged)
+      //     * r3
+      //       * r4
+      //         *r5
+      PushOneCommit.Result r = createChange();
+      PushOneCommit.Result r2 = createChange();
+      PushOneCommit.Result r3 = createChange();
+      PushOneCommit.Result r4 = createChange();
+      PushOneCommit.Result r5 = createChange();
+
+      // Approve and submit the first change
+      RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
+      revision.review(ReviewInput.approve());
+      revision.submit();
+      testRepo.reset("HEAD~1");
+
+      // Create r2.2
+      gApi.changes()
+          .id(r2.getChangeId())
+          .edit()
+          .modifyFile(file, RawInputUtil.create(newContent.getBytes(UTF_8)));
+      gApi.changes().id(r2.getChangeId()).edit().publish();
+      // Approve and submit r2.2
+      revision = gApi.changes().id(r2.getChangeId()).current();
+      revision.review(ReviewInput.approve());
+      revision.submit();
+
+      // Add an approval whose score should be copied on trivial rebase
+      gApi.changes().id(r3.getChangeId()).current().review(ReviewInput.recommend());
+
+      // Rebase the chain through r4.
+      verifyRebaseChainResponse(gApi.changes().id(r4.getChangeId()).rebaseChain(), false, r3, r4);
+
+      // Only r3 and r4 are rebased.
+      verifyRebaseForChange(r3.getChange().getId(), r2.getChange().getId(), true);
+      verifyRebaseForChange(r4.getChange().getId(), r3.getChange().getId(), false);
+
+      verifyChangeIsUpToDate(r2);
+      verifyChangeIsUpToDate(r3);
+      verifyChangeIsUpToDate(r4);
+
+      // r5 wasn't rebased.
+      assertThat(
+              gApi.changes()
+                  .id(r5.getChangeId())
+                  .get(CURRENT_REVISION)
+                  .getCurrentRevision()
+                  ._number)
+          .isEqualTo(1);
+
+      // Rebasing r5
+      verifyRebaseChainResponse(
+          gApi.changes().id(r5.getChangeId()).rebaseChain(), false, r3, r4, r5);
+
+      verifyRebaseForChange(r5.getChange().getId(), r4.getChange().getId(), false);
+    }
+
+    @Test
     public void rebaseChainWithConflicts_conflictsForbidden() throws Exception {
       PushOneCommit.Result r1 = createChange();
       gApi.changes()