Support rebasing change edits with diff3 format

Release-Notes: skip
Bug: Google b/364179875
Change-Id: Ib3dbce6dd2469adacabba4d4895266957706e804
Signed-off-by: Edwin Kempin <ekempin@google.com>
(cherry picked from commit 9b672c0f0d600f4d99b5e73db3e15dbbb07b6189)
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index 52db66f..0375cdf 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.edit.tree.ChangeFileContentModification;
 import com.google.gerrit.server.edit.tree.DeleteFileModification;
 import com.google.gerrit.server.edit.tree.RenameFileModification;
@@ -77,6 +78,7 @@
 import org.eclipse.jgit.dircache.InvalidPathException;
 import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
@@ -112,9 +114,11 @@
   private final ProjectCache projectCache;
   private final NoteDbEdits noteDbEdits;
   private final ChangeUtil changeUtil;
+  private final boolean useDiff3;
 
   @Inject
   ChangeEditModifier(
+      @GerritServerConfig Config cfg,
       @GerritPersonIdent PersonIdent gerritIdent,
       ChangeIndexer indexer,
       Provider<CurrentUser> currentUser,
@@ -132,6 +136,9 @@
     this.projectCache = projectCache;
     noteDbEdits = new NoteDbEdits(gitReferenceUpdated, zoneId, indexer, currentUser);
     this.changeUtil = changeUtil;
+    this.useDiff3 =
+        cfg.getBoolean(
+            "change", /* subsection= */ null, "diff3ConflictView", /* defaultValue= */ false);
   }
 
   /**
@@ -560,7 +567,7 @@
     return newTreeId;
   }
 
-  private static ObjectId merge(
+  private ObjectId merge(
       Repository repository,
       ChangeEdit changeEdit,
       RevCommit basePatchSetCommit,
@@ -616,7 +623,7 @@
                 "EDIT",
                 revWalk.parseCommit(editCommitId),
                 mergeResults,
-                /* diff3Format= */ false);
+                useDiff3);
         objectInserter.flush();
       }
       return newTreeId;
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index e1e384a..ba59f5f 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.TestProjectInput;
 import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
 import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
@@ -350,6 +351,52 @@
   }
 
   @Test
+  @GerritConfig(name = "change.diff3ConflictView", value = "true")
+  public void rebaseEditWithConflictsAllowedUsingDiff3() throws Exception {
+    // Create change where FILE_NAME has OLD_CONTENT
+    String changeId = newChange(admin.newIdent());
+
+    PatchSet previousPatchSet = getCurrentPatchSet(changeId);
+    createEmptyEditFor(changeId);
+    gApi.changes().id(changeId).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
+
+    // add new patch set that touches the same file as the edit
+    addNewPatchSetWithModifiedFile(changeId, FILE_NAME, new String(CONTENT_NEW2, UTF_8));
+    PatchSet currentPatchSet = getCurrentPatchSet(changeId);
+
+    Optional<EditInfo> originalEdit = getEdit(changeId);
+    assertThat(originalEdit).value().baseRevision().isEqualTo(previousPatchSet.commitId().name());
+
+    Timestamp beforeRebase = originalEdit.get().commit.committer.date;
+
+    RebaseChangeEditInput input = new RebaseChangeEditInput();
+    input.allowConflicts = true;
+    gApi.changes().id(changeId).edit().rebase(input);
+
+    ensureSameBytes(
+        getFileContentOfEdit(changeId, FILE_NAME),
+        String.format(
+                "<<<<<<< PATCH SET (%s %s)\n"
+                    + "%s\n"
+                    + "||||||| BASE\n"
+                    + "%s\n"
+                    + "=======\n"
+                    + "%s\n"
+                    + ">>>>>>> EDIT      (%s %s)\n",
+                ObjectIds.abbreviateName(currentPatchSet.commitId(), 6),
+                gApi.changes().id(changeId).get().subject,
+                CONTENT_NEW2_STR,
+                CONTENT_OLD_STR,
+                CONTENT_NEW_STR,
+                ObjectIds.abbreviateName(ObjectId.fromString(originalEdit.get().commit.commit), 6),
+                originalEdit.get().commit.subject)
+            .getBytes(UTF_8));
+    Optional<EditInfo> rebasedEdit = getEdit(changeId);
+    assertThat(rebasedEdit).value().baseRevision().isEqualTo(currentPatchSet.commitId().name());
+    assertThat(rebasedEdit).value().commit().committer().date().isNotEqualTo(beforeRebase);
+  }
+
+  @Test
   public void rebaseEditAfterUpdatingPreferredEmail() throws Exception {
     String emailOne = "email1@example.com";
     Account.Id testUser = accountOperations.newAccount().preferredEmail(emailOne).create();