Merge "Add support for using diff3 for rebasing and cherry-pick with conflicts"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 72647cb..4d2fd35 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1730,10 +1730,12 @@
 
 [[change.diff3ConflictView]]change.diff3ConflictView::
 +
-Use the diff3 formatter for merge commits with conflicts. With diff3 when
-the conflicts are shown in the "Auto Merge" view, the base section from the
-common parents will be shown as well.
+Use the diff3 formatter for merge, rebase and cherry-picking commits with conflicts.
++
+For merge commits with diff3 when the conflicts are shown in the "Auto Merge" view,
+the base section from the common parents will be shown as well.
 This setting takes effect when generating the automerge, which happens on upload.
+Also the setting takes effect when using rebase or cherry-picking in Gerrit Web UI.
 Changing the setting leaves existing changes unaffected.
 +
 Default is `false`.
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index a430034..2b6f6c6 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -40,6 +40,7 @@
 import com.google.gerrit.server.IdentifiedUser.GenericFactory;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.change.RebaseUtil.Base;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.git.GroupCollector;
@@ -67,6 +68,7 @@
 import org.eclipse.jgit.diff.Sequence;
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.merge.MergeResult;
@@ -121,6 +123,7 @@
   private ImmutableListMultimap<String, String> validationOptions = ImmutableListMultimap.of();
   private String mergeStrategy;
   private boolean verifyNeedsRebase = true;
+  private final boolean useDiff3;
 
   private CodeReviewCommit rebasedCommit;
   private PatchSet.Id rebasedPatchSetId;
@@ -136,6 +139,7 @@
       ChangeNotes.Factory notesFactory,
       GenericFactory identifiedUserFactory,
       ProjectCache projectCache,
+      @GerritServerConfig Config cfg,
       @Assisted ChangeNotes notes,
       @Assisted PatchSet originalPatchSet,
       @Assisted ObjectId baseCommitId) {
@@ -147,6 +151,7 @@
         notesFactory,
         identifiedUserFactory,
         projectCache,
+        cfg,
         notes,
         originalPatchSet);
     this.baseCommitId = baseCommitId;
@@ -162,6 +167,7 @@
       ChangeNotes.Factory notesFactory,
       GenericFactory identifiedUserFactory,
       ProjectCache projectCache,
+      @GerritServerConfig Config cfg,
       @Assisted ChangeNotes notes,
       @Assisted PatchSet originalPatchSet,
       @Assisted Change.Id baseChangeId) {
@@ -173,6 +179,7 @@
         notesFactory,
         identifiedUserFactory,
         projectCache,
+        cfg,
         notes,
         originalPatchSet);
     this.baseChangeId = baseChangeId;
@@ -187,6 +194,7 @@
       ChangeNotes.Factory notesFactory,
       GenericFactory identifiedUserFactory,
       ProjectCache projectCache,
+      @GerritServerConfig Config cfg,
       ChangeNotes notes,
       PatchSet originalPatchSet) {
     this.patchSetInserterFactory = patchSetInserterFactory;
@@ -199,6 +207,7 @@
     this.notes = notes;
     this.projectName = notes.getProjectName();
     this.originalPatchSet = originalPatchSet;
+    this.useDiff3 = cfg.getBoolean("change", null, "diff3ConflictView", false);
   }
 
   @CanIgnoreReturnValue
@@ -555,7 +564,8 @@
               original,
               "BASE",
               ctx.getRevWalk().parseCommit(base),
-              mergeResults);
+              mergeResults,
+              useDiff3);
       logger.atFine().log(
           "tree of rebased commit: %s (with conflicts, inserter: %s)",
           tree.name(), ctx.getInserter());
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index 1adbb67..86893c1 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -225,6 +225,34 @@
       boolean allowConflicts)
       throws IOException, MergeIdenticalTreeException, MergeConflictException,
           MethodNotAllowedException, InvalidMergeStrategyException {
+    return createCherryPickFromCommit(
+        inserter,
+        repoConfig,
+        mergeTip,
+        originalCommit,
+        cherryPickCommitterIdent,
+        commitMsg,
+        rw,
+        parentIndex,
+        ignoreIdenticalTree,
+        allowConflicts,
+        false);
+  }
+
+  public CodeReviewCommit createCherryPickFromCommit(
+      ObjectInserter inserter,
+      Config repoConfig,
+      RevCommit mergeTip,
+      RevCommit originalCommit,
+      PersonIdent cherryPickCommitterIdent,
+      String commitMsg,
+      CodeReviewRevWalk rw,
+      int parentIndex,
+      boolean ignoreIdenticalTree,
+      boolean allowConflicts,
+      boolean diff3Format)
+      throws IOException, MergeIdenticalTreeException, MergeConflictException,
+          MethodNotAllowedException, InvalidMergeStrategyException {
 
     ThreeWayMerger m = newThreeWayMerger(inserter, repoConfig);
     m.setBase(originalCommit.getParent(parentIndex));
@@ -300,7 +328,15 @@
 
       tree =
           mergeWithConflicts(
-              rw, inserter, dc, "HEAD", mergeTip, "CHANGE", originalCommit, mergeResults);
+              rw,
+              inserter,
+              dc,
+              "HEAD",
+              mergeTip,
+              "CHANGE",
+              originalCommit,
+              mergeResults,
+              diff3Format);
       logger.atFine().log(
           "AutoMerge treeId=%s (with conflicts, inserter: %s)", tree.name(), inserter);
     }
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 63edb7b..d828e87 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -45,6 +45,7 @@
 import com.google.gerrit.server.change.ResetCherryPickOp;
 import com.google.gerrit.server.change.SetCherryPickOp;
 import com.google.gerrit.server.change.ValidationOptionsUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.git.CommitUtil;
@@ -78,6 +79,7 @@
 import java.util.Optional;
 import java.util.Set;
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectReader;
@@ -116,11 +118,13 @@
   private final ApprovalsUtil approvalsUtil;
   private final NotifyResolver notifyResolver;
   private final BatchUpdate.Factory batchUpdateFactory;
+  private final boolean useDiff3;
 
   @Inject
   CherryPickChange(
       Sequences seq,
       Provider<InternalChangeQuery> queryProvider,
+      @GerritServerConfig Config cfg,
       @GerritPersonIdent PersonIdent myIdent,
       GitRepositoryManager gitManager,
       Provider<IdentifiedUser> user,
@@ -147,6 +151,7 @@
     this.approvalsUtil = approvalsUtil;
     this.notifyResolver = notifyResolver;
     this.batchUpdateFactory = batchUpdateFactory;
+    this.useDiff3 = cfg.getBoolean("change", null, "diff3ConflictView", false);
   }
 
   /**
@@ -376,7 +381,8 @@
                 revWalk,
                 input.parent - 1,
                 input.allowEmpty,
-                input.allowConflicts);
+                input.allowConflicts,
+                useDiff3);
         logger.atFine().log("flushing inserter %s", oi);
         oi.flush();
       } catch (MergeIdenticalTreeException | MergeConflictException e) {
diff --git a/java/com/google/gerrit/server/submit/CherryPick.java b/java/com/google/gerrit/server/submit/CherryPick.java
index 7fe5e69..a213f28 100644
--- a/java/com/google/gerrit/server/submit/CherryPick.java
+++ b/java/com/google/gerrit/server/submit/CherryPick.java
@@ -84,9 +84,11 @@
     private PatchSet.Id psId;
     private CodeReviewCommit newCommit;
     private PatchSetInfo patchSetInfo;
+    private final boolean useDiff3;
 
     private CherryPickOneOp(CodeReviewCommit toMerge) {
       super(CherryPick.this.args, toMerge);
+      this.useDiff3 = args.cfg.getBoolean("change", null, "diff3ConflictView", false);
     }
 
     @Override
@@ -119,7 +121,8 @@
                 args.rw,
                 0,
                 false,
-                false);
+                false,
+                useDiff3);
       } catch (MergeConflictException mce) {
         // Keep going in the case of a single merge failure; the goal is to
         // cherry-pick as many commits as possible.
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategy.java b/java/com/google/gerrit/server/submit/SubmitStrategy.java
index d4dd67a..5346acc 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategy.java
@@ -38,6 +38,7 @@
 import com.google.gerrit.server.change.RebaseChangeOp;
 import com.google.gerrit.server.change.SetPrivateOp;
 import com.google.gerrit.server.change.TestSubmitInput;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.extensions.events.ChangeMerged;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
@@ -66,6 +67,7 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
@@ -115,6 +117,7 @@
     final EmailMerge.Factory mergedSenderFactory;
     final GitRepositoryManager repoManager;
     final LabelNormalizer labelNormalizer;
+    final Config cfg;
     final PatchSetInfoFactory patchSetInfoFactory;
     final PatchSetUtil psUtil;
     final ProjectCache projectCache;
@@ -158,6 +161,7 @@
         MergeUtilFactory mergeUtilFactory,
         PatchSetInfoFactory patchSetInfoFactory,
         PatchSetUtil psUtil,
+        @GerritServerConfig Config cfg,
         @GerritPersonIdent PersonIdent serverIdent,
         ProjectCache projectCache,
         RebaseChangeOp.Factory rebaseFactory,
@@ -189,6 +193,7 @@
       this.cmUtil = cmUtil;
       this.labelNormalizer = labelNormalizer;
       this.projectConfigFactory = projectConfigFactory;
+      this.cfg = cfg;
       this.patchSetInfoFactory = patchSetInfoFactory;
       this.psUtil = psUtil;
       this.projectCache = projectCache;