Support diff3 conflict style in merges

Add conflict style setters to commands that can produce
merge conflicts and default to merge.conflictStyle from
the Git config. Propagate the setting into the merger
and select the appropriate formatter in ResolveMerger.

Activate existing diff3 support and add tests for the new behavior.

Bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=442284
Change-Id: Ic201492b2f086712d61fa61dc2c53fcbe5de46b3
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
index 3f5c5da..cb91b12 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
@@ -11,6 +11,8 @@
 
 import static org.eclipse.jgit.api.CherryPickCommitMessageProvider.ORIGINAL;
 import static org.eclipse.jgit.api.CherryPickCommitMessageProvider.ORIGINAL_WITH_REFERENCE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -22,6 +24,7 @@
 import java.util.Iterator;
 
 import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus;
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.ResetCommand.ResetType;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -396,6 +399,23 @@ public void testCherryPickConflictMarkers() throws Exception {
 	}
 
 	@Test
+	public void testCherryPickConflictDiff3Markers() throws Exception {
+		try (Git git = new Git(db)) {
+			RevCommit sideCommit = prepareCherryPick(git);
+
+			db.getConfig().setEnum(CONFIG_MERGE_SECTION, null,
+					CONFIG_KEY_CONFLICTSTYLE, ConflictStyle.DIFF3);
+
+			CherryPickResult result = git.cherryPick()
+					.include(sideCommit.getId()).call();
+			assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
+
+			String expected = "<<<<<<< master\na(master)\n||||||| BASE\na\n=======\na(side)\n>>>>>>> 527460a side\n";
+			checkFile(new File(db.getWorkTree(), "a"), expected);
+		}
+	}
+
+	@Test
 	public void testCherryPickConflictFiresModifiedEvent() throws Exception {
 		ListenerHandle listener = null;
 		try (Git git = new Git(db)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index 1ec5067..086e261 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -10,6 +10,8 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.eclipse.jgit.lib.Constants.MASTER;
 import static org.eclipse.jgit.lib.Constants.R_HEADS;
 import static org.junit.Assert.assertEquals;
@@ -27,6 +29,7 @@
 import java.util.Iterator;
 import java.util.regex.Pattern;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.ResetCommand.ResetType;
@@ -2220,4 +2223,37 @@ private void checkMergeFailedResult(final MergeResult result,
 		assertEquals(null, result.getConflicts());
 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
 	}
+
+	@Test
+	public void testDiff3ConflictStyle() throws Exception {
+		try (Git git = new Git(db)) {
+			writeTrashFile("a", "1\na\n3\n");
+			git.add().addFilepattern("a").call();
+			RevCommit initialCommit = git.commit().setMessage("initial").call();
+
+			createBranch(initialCommit, "refs/heads/side");
+			checkoutBranch("refs/heads/side");
+
+			writeTrashFile("a", "1\na(side)\n3\n");
+			git.add().addFilepattern("a").call();
+			RevCommit secondCommit = git.commit().setMessage("side").call();
+
+			checkoutBranch("refs/heads/master");
+
+			writeTrashFile("a", "1\na(main)\n3\n");
+			git.add().addFilepattern("a").call();
+			git.commit().setMessage("main").call();
+
+			db.getConfig().setEnum(CONFIG_MERGE_SECTION, null,
+					CONFIG_KEY_CONFLICTSTYLE, ConflictStyle.DIFF3);
+
+			MergeResult result = git.merge().include(secondCommit.getId())
+					.setStrategy(MergeStrategy.RESOLVE).call();
+			assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+
+			assertEquals(
+					"1\n<<<<<<< HEAD\na(main)\n||||||| BASE\na\n=======\na(side)\n>>>>>>> d97aebf6e0bbdb3f21f8a22ec8cbf1ac24d986d8\n3\n",
+					read(new File(db.getWorkTree(), "a")));
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index 695681d..5c33f03 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -10,6 +10,8 @@
 package org.eclipse.jgit.api;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -25,6 +27,7 @@
 import java.util.concurrent.Callable;
 
 import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.errors.NoHeadException;
 import org.eclipse.jgit.junit.JGitTestUtil;
@@ -163,6 +166,40 @@ public void testPullConflict() throws Exception {
 	}
 
 	@Test
+	public void testPullConflictDiff3() throws Exception {
+		PullResult res = target.pull().call();
+		// nothing to update since we don't have different data yet
+		assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty());
+		assertTrue(res.getMergeResult().getMergeStatus()
+				.equals(MergeStatus.ALREADY_UP_TO_DATE));
+
+		assertFileContentsEqual(targetFile, "Hello world");
+
+		// change the source file
+		writeToFile(sourceFile, "Source change");
+		source.add().addFilepattern("SomeFile.txt").call();
+		source.commit().setMessage("Source change in remote").call();
+
+		// change the target file
+		writeToFile(targetFile, "Target change");
+		target.add().addFilepattern("SomeFile.txt").call();
+		target.commit().setMessage("Target change in local").call();
+
+		target.getRepository().getConfig().setEnum(CONFIG_MERGE_SECTION, null,
+				CONFIG_KEY_CONFLICTSTYLE, ConflictStyle.DIFF3);
+
+		res = target.pull().call();
+
+		String sourceChangeString = "Source change\n>>>>>>> branch 'master' of "
+				+ target.getRepository().getConfig().getString("remote",
+						"origin", "url");
+
+		assertFileContentsEqual(targetFile, "<<<<<<< HEAD\n" + "Target change\n"
+				+ "||||||| BASE\n" + "Hello world\n" + "=======\n"
+				+ sourceChangeString + "\n");
+	}
+
+	@Test
 	public void testPullConflictTheirs() throws Exception {
 		PullResult res = target.pull().call();
 		// nothing to update since we don't have different data yet
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 4c8cf06..d99c285 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -10,6 +10,8 @@
 package org.eclipse.jgit.api;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -30,6 +32,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
 import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler2;
@@ -371,6 +374,43 @@ public void testRebaseNoMergeBaseConflict() throws Exception {
 	}
 
 	/**
+	 * Create a commit A and an unrelated commit B creating the same file with
+	 * different content. Then rebase A onto B. The rebase should stop with a
+	 * conflict and the conflict style should be DIFF3.
+	 *
+	 * @throws Exception on errors
+	 */
+	@Test
+	public void testRebaseNoMergeBaseConflictDiff3() throws Exception {
+		writeTrashFile(FILE1, FILE1);
+		git.add().addFilepattern(FILE1).call();
+		RevCommit first = git.commit().setMessage("Add file").call();
+		File file1 = new File(db.getWorkTree(), FILE1);
+		assertTrue(file1.exists());
+		// Create an independent branch
+		git.checkout().setOrphan(true).setName("orphan").call();
+		git.rm().addFilepattern(FILE1).call();
+		assertFalse(file1.exists());
+		writeTrashFile(FILE1, "something else");
+		git.add().addFilepattern(FILE1).call();
+		git.commit().setMessage("Orphan").call();
+		checkoutBranch("refs/heads/master");
+		assertEquals(first.getId(), db.resolve("HEAD"));
+
+		db.getConfig().setEnum(CONFIG_MERGE_SECTION, null,
+				CONFIG_KEY_CONFLICTSTYLE, ConflictStyle.DIFF3);
+
+		RebaseResult res = git.rebase().setUpstream("refs/heads/orphan").call();
+		assertEquals(Status.STOPPED, res.getStatus());
+		assertEquals(first, res.getCurrentCommit());
+		checkFile(file1,
+				"<<<<<<< Upstream, based on orphan\n" + "something else\n"
+						+ "||||||| BASE\n" + "=======\n" + "file1\n"
+						+ ">>>>>>> "
+						+ first.abbreviate(7).name() + " Add file\n");
+	}
+
+	/**
 	 * Create the following commits and then attempt to rebase topic onto
 	 * master. This will serialize the branches.
 	 *
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
index 89fdb32..a7a1dff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
@@ -9,6 +9,8 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -20,6 +22,7 @@
 import java.io.IOException;
 import java.util.Iterator;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.ResetCommand.ResetType;
 import org.eclipse.jgit.api.errors.GitAPIException;
@@ -377,6 +380,25 @@ public void testRevertConflictMarkers() throws Exception {
 	}
 
 	@Test
+	public void testRevertConflictMarkersDiff3() throws Exception {
+		try (Git git = new Git(db)) {
+			RevCommit sideCommit = prepareRevert(git);
+
+			db.getConfig().setEnum(CONFIG_MERGE_SECTION, null,
+					CONFIG_KEY_CONFLICTSTYLE, ConflictStyle.DIFF3);
+
+			RevertCommand revert = git.revert();
+			RevCommit newHead = revert.include(sideCommit.getId()).call();
+			assertNull(newHead);
+			MergeResult result = revert.getFailingResult();
+			assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+
+			String expected = "<<<<<<< master\na(latest)\n||||||| BASE\na(previous)\n=======\na\n>>>>>>> ca96c31 second master\n";
+			checkFile(new File(db.getWorkTree(), "a"), expected);
+		}
+	}
+
+	@Test
 	public void testRevertOurCommitName() throws Exception {
 		try (Git git = new Git(db)) {
 			RevCommit sideCommit = prepareRevert(git);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
index 49b31b1..b169fac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
@@ -9,15 +9,19 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.File;
 import java.text.MessageFormat;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.errors.InvalidRefNameException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.api.errors.NoHeadException;
@@ -429,6 +433,43 @@ public void stashedContentMerge() throws Exception {
 	}
 
 	@Test
+	public void stashedContentMergeDiff3() throws Exception {
+		writeTrashFile(PATH, "content\nmore content\n");
+		git.add().addFilepattern(PATH).call();
+		git.commit().setMessage("more content").call();
+
+		writeTrashFile(PATH, "content\nhead change\nmore content\n");
+		git.add().addFilepattern(PATH).call();
+		git.commit().setMessage("even content").call();
+
+		writeTrashFile(PATH, "content\nstashed change\nmore content\n");
+
+		db.getConfig().setEnum(CONFIG_MERGE_SECTION, null,
+				CONFIG_KEY_CONFLICTSTYLE, ConflictStyle.DIFF3);
+
+		RevCommit stashed = git.stashCreate().call();
+		assertNotNull(stashed);
+		assertEquals("content\nhead change\nmore content\n",
+				read(committedFile));
+		assertTrue(git.status().call().isClean());
+		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
+
+		writeTrashFile(PATH, "content\nmore content\ncommitted change\n");
+		git.add().addFilepattern(PATH).call();
+		git.commit().setMessage("committed change").call();
+		recorder.assertNoEvent();
+
+		assertThrows(StashApplyFailureException.class,
+				() -> git.stashApply().call());
+		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
+		Status status = new StatusCommand(db).call();
+		assertEquals(1, status.getConflicting().size());
+		assertEquals(
+				"content\n<<<<<<< HEAD\n||||||| stashed HEAD\nhead change\n=======\nstashed change\n>>>>>>> stash\nmore content\ncommitted change\n",
+				read(PATH));
+	}
+
+	@Test
 	public void stashedContentMergeXtheirs() throws Exception {
 		writeTrashFile(PATH, "content\nmore content\n");
 		git.add().addFilepattern(PATH).call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index c6a6321..b5e4b21 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -11,6 +11,8 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.time.Instant.EPOCH;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -30,6 +32,7 @@
 import java.util.Set;
 
 import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeResult;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.RebaseResult;
@@ -964,6 +967,39 @@ public void checkContentMergeConflict(MergeStrategy strategy)
 	}
 
 	@Theory
+	public void checkContentMergeConflictDiff3(MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("file", "1\n2\n3");
+		git.add().addFilepattern("file").call();
+		RevCommit first = git.commit().setMessage("added file").call();
+
+		writeTrashFile("file", "1master\n2\n3");
+		git.commit().setAll(true).setMessage("modified file on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("file", "1side\n2\n3");
+		RevCommit sideCommit = git.commit().setAll(true)
+				.setMessage("modified file on side").call();
+
+		git.checkout().setName("master").call();
+
+		db.getConfig().setEnum(CONFIG_MERGE_SECTION, null,
+				CONFIG_KEY_CONFLICTSTYLE, ConflictStyle.DIFF3);
+
+		MergeResult result = git.merge().setStrategy(strategy)
+				.include(sideCommit).call();
+		assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+		String expected = "<<<<<<< HEAD\n" + "1master\n" + "||||||| BASE\n"
+				+ "1\n" + "=======\n" + "1side\n" + ">>>>>>> "
+				+ sideCommit.name() + "\n" + "2\n"
+				+ "3";
+		assertEquals(expected, read("file"));
+	}
+
+	@Theory
 	public void checkContentMergeConflict_noTree(MergeStrategy strategy)
 			throws Exception {
 		Git git = Git.wrap(db);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
index a353d1a..6761f1e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
@@ -10,6 +10,9 @@
 package org.eclipse.jgit.api;
 
 import static org.eclipse.jgit.api.CherryPickCommitMessageProvider.ORIGINAL;
+import static org.eclipse.jgit.api.MergeCommand.ConflictStyle.MERGE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
 
 import java.io.IOException;
@@ -18,6 +21,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -73,6 +77,8 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
 
 	private ContentMergeStrategy contentStrategy;
 
+	private ConflictStyle conflictStyle;
+
 	private Integer mainlineParentNumber;
 
 	private boolean noCommit = false;
@@ -138,9 +144,9 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
 				boolean noProblems;
 				Map<String, MergeFailureReason> failingPaths = null;
 				List<String> unmergedPaths = null;
-				if (merger instanceof ResolveMerger) {
-					ResolveMerger resolveMerger = (ResolveMerger) merger;
+				if (merger instanceof ResolveMerger resolveMerger) {
 					resolveMerger.setContentMergeStrategy(contentStrategy);
+					resolveMerger.setConflictStyle(getConflictStyle());
 					resolveMerger.setCommitNames(
 							new String[] { "BASE", ourName, cherryPickName }); //$NON-NLS-1$
 					resolveMerger
@@ -363,6 +369,25 @@ public CherryPickCommand setContentMergeStrategy(
 	}
 
 	/**
+	 * Sets the conflict style to be used when formatting merge conflicts.
+	 *
+	 * @param conflictStyle
+	 *            a {@link org.eclipse.jgit.api.MergeCommand.ConflictStyle}
+	 * @return {@code this}
+	 * @since 7.6
+	 */
+	public CherryPickCommand setConflictStyle(ConflictStyle conflictStyle) {
+		this.conflictStyle = conflictStyle;
+		return this;
+	}
+
+	private ConflictStyle getConflictStyle() {
+		return conflictStyle != null ? conflictStyle
+				: repo.getConfig().getEnum(CONFIG_MERGE_SECTION, null,
+						CONFIG_KEY_CONFLICTSTYLE, MERGE);
+	}
+
+	/**
 	 * Set the (1-based) parent number to diff against
 	 *
 	 * @param mainlineParentNumber
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index 7064f5a..a79fbf7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -75,6 +75,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
 
 	private ContentMergeStrategy contentStrategy;
 
+	private ConflictStyle conflictStyle;
+
 	private List<Ref> commits = new ArrayList<>();
 
 	private Boolean squash;
@@ -336,9 +338,9 @@ public MergeResult call() throws GitAPIException, NoHeadException,
 				Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults = null;
 				Map<String, MergeFailureReason> failingPaths = null;
 				List<String> unmergedPaths = null;
-				if (merger instanceof ResolveMerger) {
-					ResolveMerger resolveMerger = (ResolveMerger) merger;
+				if (merger instanceof ResolveMerger resolveMerger) {
 					resolveMerger.setContentMergeStrategy(contentStrategy);
+					resolveMerger.setConflictStyle(conflictStyle);
 					resolveMerger.setCommitNames(new String[] {
 							"BASE", "HEAD", ref.getName() }); //$NON-NLS-1$ //$NON-NLS-2$
 					resolveMerger.setWorkingTreeIterator(new FileTreeIterator(repo));
@@ -456,6 +458,8 @@ private void fallBackToConfiguration() {
 			commit = Boolean.valueOf(config.isCommit());
 		if (fastForwardMode == null)
 			fastForwardMode = config.getFastForwardMode();
+		if (conflictStyle == null)
+			conflictStyle = config.getConflictStyle();
 	}
 
 	private void updateHead(StringBuilder refLogMessage, ObjectId newHeadId,
@@ -511,6 +515,19 @@ public MergeCommand setContentMergeStrategy(ContentMergeStrategy strategy) {
 	}
 
 	/**
+	 * Sets the conflict style to be used when formatting merge conflicts.
+	 *
+	 * @param conflictStyle
+	 *            a {@link org.eclipse.jgit.api.MergeCommand.ConflictStyle}
+	 * @return {@code this}
+	 * @since 7.6
+	 */
+	public MergeCommand setConflictStyle(ConflictStyle conflictStyle) {
+		this.conflictStyle = conflictStyle;
+		return this;
+	}
+
+	/**
 	 * Reference to a commit to be merged with the current head
 	 *
 	 * @param aCommit
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index 4b2cee4..ff5dbff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -11,10 +11,15 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.eclipse.jgit.api.MergeCommand.ConflictStyle.MERGE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
+
 import java.io.IOException;
 import java.text.MessageFormat;
 
 import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
 import org.eclipse.jgit.api.MergeCommand.FastForwardMode.Merge;
 import org.eclipse.jgit.api.RebaseCommand.Operation;
@@ -72,6 +77,8 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
 
 	private ContentMergeStrategy contentStrategy;
 
+	private ConflictStyle conflictStyle;
+
 	private TagOpt tagOption;
 
 	private FastForwardMode fastForwardMode;
@@ -361,6 +368,7 @@ public PullResult call() throws GitAPIException,
 					.setOperation(Operation.BEGIN)
 					.setStrategy(strategy)
 					.setContentMergeStrategy(contentStrategy)
+					.setConflictStyle(getConflictStyle())
 					.setPreserveMerges(
 							pullRebaseMode == BranchRebaseMode.MERGES)
 					.call();
@@ -371,6 +379,7 @@ public PullResult call() throws GitAPIException,
 					.setProgressMonitor(monitor)
 					.setStrategy(strategy)
 					.setContentMergeStrategy(contentStrategy)
+					.setConflictStyle(getConflictStyle())
 					.setFastForward(getFastForwardMode()).call();
 			monitor.update(1);
 			result = new PullResult(fetchRes, remote, mergeRes);
@@ -464,6 +473,25 @@ public PullCommand setContentMergeStrategy(ContentMergeStrategy strategy) {
 	}
 
 	/**
+	 * Sets the conflict style to be used when formatting merge conflicts.
+	 *
+	 * @param conflictStyle
+	 *            a {@link org.eclipse.jgit.api.MergeCommand.ConflictStyle}
+	 * @return {@code this}
+	 * @since 7.6
+	 */
+	public PullCommand setConflictStyle(ConflictStyle conflictStyle) {
+		this.conflictStyle = conflictStyle;
+		return this;
+	}
+
+	private ConflictStyle getConflictStyle() {
+		return conflictStyle != null ? conflictStyle
+				: repo.getConfig().getEnum(CONFIG_MERGE_SECTION, null,
+						CONFIG_KEY_CONFLICTSTYLE, MERGE);
+	}
+
+	/**
 	 * Set the specification of annotated tag behavior during fetch
 	 *
 	 * @param tagOpt
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index ae0389c..ba8461d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -11,6 +11,9 @@
 package org.eclipse.jgit.api;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.api.MergeCommand.ConflictStyle.MERGE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -31,6 +34,7 @@
 import java.util.regex.Pattern;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.RebaseResult.Status;
 import org.eclipse.jgit.api.ResetCommand.ResetType;
 import org.eclipse.jgit.api.errors.CheckoutConflictException;
@@ -220,6 +224,8 @@ public enum Operation {
 
 	private ContentMergeStrategy contentStrategy;
 
+	private ConflictStyle conflictStyle;
+
 	private boolean preserveMerges = false;
 
 	/**
@@ -557,6 +563,7 @@ private RebaseResult cherryPickCommitFlattening(RevCommit commitToPick)
 					.setReflogPrefix(REFLOG_PREFIX)
 					.setStrategy(strategy)
 					.setContentMergeStrategy(contentStrategy)
+						.setConflictStyle(getConflictStyle())
 					.call();
 				switch (cherryPickResult.getStatus()) {
 				case FAILED:
@@ -611,7 +618,8 @@ private RebaseResult cherryPickCommitPreservingMerges(RevCommit commitToPick)
 							.setOurCommitName(ourCommitName)
 							.setReflogPrefix(REFLOG_PREFIX)
 							.setStrategy(strategy)
-							.setContentMergeStrategy(contentStrategy);
+							.setContentMergeStrategy(contentStrategy)
+							.setConflictStyle(getConflictStyle());
 					if (isMerge) {
 						pickCommand.setMainlineParentNumber(1);
 						// We write a MERGE_HEAD and later commit explicitly
@@ -649,6 +657,7 @@ private RebaseResult cherryPickCommitPreservingMerges(RevCommit commitToPick)
 							.setProgressMonitor(monitor)
 							.setStrategy(strategy)
 							.setContentMergeStrategy(contentStrategy)
+							.setConflictStyle(getConflictStyle())
 							.setCommit(false);
 					for (int i = 1; i < commitToPick.getParentCount(); i++)
 						merge.include(newParents.get(i));
@@ -1700,6 +1709,25 @@ public RebaseCommand setContentMergeStrategy(ContentMergeStrategy strategy) {
 	}
 
 	/**
+	 * Sets the conflict style to be used when formatting merge conflicts.
+	 *
+	 * @param conflictStyle
+	 *            a {@link org.eclipse.jgit.api.MergeCommand.ConflictStyle}
+	 * @return {@code this}
+	 * @since 7.6
+	 */
+	public RebaseCommand setConflictStyle(ConflictStyle conflictStyle) {
+		this.conflictStyle = conflictStyle;
+		return this;
+	}
+
+	private ConflictStyle getConflictStyle() {
+		return conflictStyle != null ? conflictStyle
+				: repo.getConfig().getEnum(CONFIG_MERGE_SECTION, null,
+						CONFIG_KEY_CONFLICTSTYLE, MERGE);
+	}
+
+	/**
 	 * Whether to preserve merges during rebase
 	 *
 	 * @param preserve
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 6643c83..5a107d5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -9,6 +9,9 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.eclipse.jgit.api.MergeCommand.ConflictStyle.MERGE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
 
 import java.io.IOException;
@@ -17,6 +20,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
 import org.eclipse.jgit.api.errors.GitAPIException;
@@ -74,6 +78,8 @@ public class RevertCommand extends GitCommand<RevCommit> {
 
 	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
 
+	private ConflictStyle conflictStyle;
+
 	/**
 	 * <p>
 	 * Constructor for RevertCommand.
@@ -138,6 +144,7 @@ public RevCommit call() throws NoMessageException, UnmergedPathsException,
 						+ srcCommit.getShortMessage();
 
 				ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo);
+				merger.setConflictStyle(getConflictStyle());
 				merger.setWorkingTreeIterator(new FileTreeIterator(repo));
 				merger.setBase(srcCommit.getTree());
 				merger.setCommitNames(new String[] {
@@ -344,4 +351,23 @@ public RevertCommand setInsertChangeId(boolean insertChangeId) {
 		return this;
 	}
 
+	/**
+	 * Sets the conflict style to be used when formatting merge conflicts.
+	 *
+	 * @param conflictStyle
+	 *            a {@link org.eclipse.jgit.api.MergeCommand.ConflictStyle}
+	 * @return {@code this}
+	 * @since 7.6
+	 */
+	public RevertCommand setConflictStyle(ConflictStyle conflictStyle) {
+		this.conflictStyle = conflictStyle;
+		return this;
+	}
+
+	private ConflictStyle getConflictStyle() {
+		return conflictStyle != null ? conflictStyle
+				: repo.getConfig().getEnum(CONFIG_MERGE_SECTION, null,
+						CONFIG_KEY_CONFLICTSTYLE, MERGE);
+	}
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index b0b715e..aa733b5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -9,6 +9,9 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.eclipse.jgit.api.MergeCommand.ConflictStyle.MERGE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP;
 
 import java.io.IOException;
@@ -17,6 +20,7 @@
 import java.util.List;
 import java.util.Set;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidRefNameException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -76,6 +80,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
 
 	private ContentMergeStrategy contentStrategy;
 
+	private ConflictStyle conflictStyle;
+
 	/**
 	 * Create command to apply the changes of a stashed commit
 	 *
@@ -173,8 +179,7 @@ public ObjectId call() throws GitAPIException,
 
 			Merger merger = strategy.newMerger(repo);
 			boolean mergeSucceeded;
-			if (merger instanceof ResolveMerger) {
-				ResolveMerger resolveMerger = (ResolveMerger) merger;
+			if (merger instanceof ResolveMerger resolveMerger) {
 				resolveMerger
 						.setCommitNames(new String[] { "stashed HEAD", "HEAD", //$NON-NLS-1$ //$NON-NLS-2$
 								"stash" }); //$NON-NLS-1$
@@ -182,6 +187,7 @@ public ObjectId call() throws GitAPIException,
 				resolveMerger
 						.setWorkingTreeIterator(new FileTreeIterator(repo));
 				resolveMerger.setContentMergeStrategy(contentStrategy);
+				resolveMerger.setConflictStyle(getConflictStyle());
 				mergeSucceeded = resolveMerger.merge(headCommit, stashCommit);
 				List<String> modifiedByMerge = resolveMerger.getModifiedFiles();
 				if (!modifiedByMerge.isEmpty()) {
@@ -199,12 +205,12 @@ public ObjectId call() throws GitAPIException,
 				dco.checkout(); // Ignoring failed deletes....
 				if (restoreIndex) {
 					Merger ixMerger = strategy.newMerger(repo, true);
-					if (ixMerger instanceof ResolveMerger) {
-						ResolveMerger resolveMerger = (ResolveMerger) ixMerger;
+					if (ixMerger instanceof ResolveMerger resolveMerger) {
 						resolveMerger.setCommitNames(new String[] { "stashed HEAD", //$NON-NLS-1$
 								"HEAD", "stashed index" }); //$NON-NLS-1$//$NON-NLS-2$
 						resolveMerger.setBase(stashHeadCommit);
 						resolveMerger.setContentMergeStrategy(contentStrategy);
+						resolveMerger.setConflictStyle(getConflictStyle());
 					}
 					boolean ok = ixMerger.merge(headCommit, stashIndexCommit);
 					if (ok) {
@@ -218,8 +224,7 @@ public ObjectId call() throws GitAPIException,
 
 				if (untrackedCommit != null) {
 					Merger untrackedMerger = strategy.newMerger(repo, true);
-					if (untrackedMerger instanceof ResolveMerger) {
-						ResolveMerger resolveMerger = (ResolveMerger) untrackedMerger;
+					if (untrackedMerger instanceof ResolveMerger resolveMerger) {
 						resolveMerger.setCommitNames(new String[] { "null", "HEAD", //$NON-NLS-1$//$NON-NLS-2$
 								"untracked files" }); //$NON-NLS-1$
 						// There is no common base for HEAD & untracked files
@@ -230,6 +235,7 @@ public ObjectId call() throws GitAPIException,
 						// commit.
 						resolveMerger.setBase(null);
 						resolveMerger.setContentMergeStrategy(contentStrategy);
+						resolveMerger.setConflictStyle(getConflictStyle());
 					}
 					boolean ok = untrackedMerger.merge(headCommit,
 							untrackedCommit);
@@ -305,6 +311,25 @@ public StashApplyCommand setContentMergeStrategy(
 	}
 
 	/**
+	 * Sets the conflict style to be used when formatting merge conflicts.
+	 *
+	 * @param conflictStyle
+	 *            a {@link org.eclipse.jgit.api.MergeCommand.ConflictStyle}
+	 * @return {@code this}
+	 * @since 7.6
+	 */
+	public StashApplyCommand setConflictStyle(ConflictStyle conflictStyle) {
+		this.conflictStyle = conflictStyle;
+		return this;
+	}
+
+	private ConflictStyle getConflictStyle() {
+		return conflictStyle != null ? conflictStyle
+				: repo.getConfig().getEnum(CONFIG_MERGE_SECTION, null,
+						CONFIG_KEY_CONFLICTSTYLE, MERGE);
+	}
+
+	/**
 	 * Whether the command should restore untracked files
 	 *
 	 * @param restoreUntracked
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
index 54b7aa4..680e3e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
@@ -9,8 +9,13 @@
  *******************************************************************************/
 package org.eclipse.jgit.merge;
 
+import static org.eclipse.jgit.api.MergeCommand.ConflictStyle.MERGE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
+
 import java.io.IOException;
 
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Config.SectionParser;
@@ -60,6 +65,8 @@ public static final SectionParser<MergeConfig> getParser(
 
 	private final FastForwardMode fastForwardMode;
 
+	private final ConflictStyle conflictStyle;
+
 	private final boolean squash;
 
 	private final boolean commit;
@@ -67,12 +74,14 @@ public static final SectionParser<MergeConfig> getParser(
 	private MergeConfig(String branch, Config config) {
 		String[] mergeOptions = getMergeOptions(branch, config);
 		fastForwardMode = getFastForwardMode(config, mergeOptions);
+		conflictStyle = getConflictStyle(config);
 		squash = isMergeConfigOptionSet("--squash", mergeOptions); //$NON-NLS-1$
 		commit = !isMergeConfigOptionSet("--no-commit", mergeOptions); //$NON-NLS-1$
 	}
 
 	private MergeConfig() {
 		fastForwardMode = FastForwardMode.FF;
+		conflictStyle = ConflictStyle.MERGE;
 		squash = false;
 		commit = true;
 	}
@@ -87,6 +96,16 @@ public FastForwardMode getFastForwardMode() {
 	}
 
 	/**
+	 * Get the configured conflict style
+	 *
+	 * @return the configured conflict style
+	 * @since 7.6
+	 */
+	public ConflictStyle getConflictStyle() {
+		return conflictStyle;
+	}
+
+	/**
 	 * Whether merges into this branch are configured to be squash merges, false
 	 * otherwise
 	 *
@@ -120,6 +139,11 @@ private static FastForwardMode getFastForwardMode(Config config,
 		return ffmode;
 	}
 
+	private ConflictStyle getConflictStyle(Config config) {
+		return config.getEnum(CONFIG_MERGE_SECTION, null,
+						CONFIG_KEY_CONFLICTSTYLE, MERGE);
+	}
+
 	private static boolean isMergeConfigOptionSet(String optionToLookFor,
 			String[] mergeOptions) {
 		for (String option : mergeOptions) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index dc96f65..cb82577 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -16,9 +16,12 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.time.Instant.EPOCH;
+import static org.eclipse.jgit.api.MergeCommand.ConflictStyle.MERGE;
 import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONFLICTSTYLE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION;
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
 import java.io.Closeable;
@@ -39,6 +42,7 @@
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.MergeCommand.ConflictStyle;
 import org.eclipse.jgit.attributes.Attribute;
 import org.eclipse.jgit.attributes.Attributes;
 import org.eclipse.jgit.attributes.AttributesNodeProvider;
@@ -831,6 +835,8 @@ public enum MergeFailureReason {
 	 */
 	protected MergeAlgorithm mergeAlgorithm;
 
+	private ConflictStyle conflictStyle;
+
 	/**
 	 * The {@link ContentMergeStrategy} to use for "resolve" and "recursive"
 	 * merges.
@@ -870,6 +876,7 @@ protected ResolveMerger(Repository local, boolean inCore) {
 		super(local);
 		Config config = local.getConfig();
 		mergeAlgorithm = getMergeAlgorithm(config);
+		conflictStyle = getConflictStyle(config);
 		commitNames = defaultCommitNames();
 		this.inCore = inCore;
 	}
@@ -896,6 +903,7 @@ protected ResolveMerger(Repository local) {
 	protected ResolveMerger(ObjectInserter inserter, Config config) {
 		super(inserter);
 		mergeAlgorithm = getMergeAlgorithm(config);
+		conflictStyle = getConflictStyle(config);
 		commitNames = defaultCommitNames();
 		inCore = true;
 	}
@@ -923,6 +931,23 @@ public void setContentMergeStrategy(ContentMergeStrategy strategy) {
 				: strategy;
 	}
 
+	/**
+	 * Sets the conflict style to be used when formatting merge conflicts.
+	 *
+	 * @param conflictStyle
+	 *            a {@link org.eclipse.jgit.api.MergeCommand.ConflictStyle}
+	 * @since 7.6
+	 */
+	public void setConflictStyle(ConflictStyle conflictStyle) {
+		this.conflictStyle = conflictStyle;
+	}
+
+	private ConflictStyle getConflictStyle(Config config) {
+		return config.getEnum(CONFIG_MERGE_SECTION,
+				null,
+				CONFIG_KEY_CONFLICTSTYLE, MERGE);
+	}
+
 	@Override
 	protected boolean mergeImpl() throws IOException {
 		return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
@@ -1668,8 +1693,16 @@ private TemporaryBuffer doMerge(MergeResult<RawText> result)
 				db != null ? nonNullRepo().getDirectory() : null, workTreeUpdater.getInCoreFileSizeLimit());
 		boolean success = false;
 		try {
-			new MergeFormatter().formatMerge(buf, result,
-					Arrays.asList(commitNames), UTF_8);
+			switch (conflictStyle) {
+			case MERGE:
+				new MergeFormatter().formatMerge(buf, result,
+						Arrays.asList(commitNames), UTF_8);
+				break;
+			case DIFF3:
+				new MergeFormatter().formatMergeDiff3(buf, result,
+						Arrays.asList(commitNames), UTF_8);
+				break;
+			}
 			buf.close();
 			success = true;
 		} finally {