Handle -m option for Merge command

Set the commit message to be used for the merge commit (in case one is
created)

Bug: 442886
Change-Id: Ie5ecc13822faa366f00b3daa07f74c8441cae195
Signed-off-by: Axel Richard <axel.richard@obeo.fr>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
index 8c5066a..975e8c4 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, IBM Corporation and others.
+ * Copyright (C) 2012, 2014 IBM Corporation and others.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -45,9 +45,12 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
+import java.util.Iterator;
+
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.lib.CLIRepositoryTestCase;
 import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -209,4 +212,24 @@ public void testFastForwardOnly() throws Exception {
 		assertEquals("fatal: Not possible to fast-forward, aborting.",
 				execute("git merge master --ff-only")[0]);
 	}
+
+	@Test
+	public void testMergeWithUserMessage() throws Exception {
+		git.branchCreate().setName("side").call();
+		writeTrashFile("master", "content");
+		git.add().addFilepattern("master").call();
+		git.commit().setMessage("master commit").call();
+		git.checkout().setName("side").call();
+		writeTrashFile("side", "content");
+		git.add().addFilepattern("side").call();
+		git.commit().setMessage("side commit").call();
+
+		assertEquals("Merge made by the '" + MergeStrategy.RECURSIVE.getName()
+				+ "' strategy.",
+				execute("git merge master -m \"user message\"")[0]);
+
+		Iterator<RevCommit> it = git.log().call().iterator();
+		RevCommit newHead = it.next();
+		assertEquals("user message", newHead.getFullMessage());
+	}
 }
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index d801456..574981d 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -298,6 +298,7 @@
 usage_mergeFfOnly=Refuse to merge and exit with a non-zero status unless the current HEAD is already up-to-date or the merge can be resolved as a fast-forward.
 usage_mergeRef=Ref to be merged
 usage_mergeStrategy=Use the given merge strategy. Can be supplied more than once to specify them in the order they should be tried. If there is no -s option, the recursive strategy is used. Currently the following strategies are supported: ours, theirs, simple-two-way-in-core, resolve, recursive
+usage_message=Set the commit message to be used for the merge commit (in case one is created).
 usage_moveRenameABranch=move/rename a branch
 usage_nameStatus=show only name and status of files
 usage_noCheckoutAfterClone=no checkout of HEAD is performed after the clone is complete
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
index b3ff527..93c4388 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2011, 2014 Christian Halstrick <christian.halstrick@sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -98,6 +98,9 @@ void ffonly(@SuppressWarnings("unused") final boolean ignored) {
 		ff = FastForwardMode.FF_ONLY;
 	}
 
+	@Option(name = "-m", usage = "usage_message")
+	private String message;
+
 	@Override
 	protected void run() throws Exception {
 		if (squash && ff == FastForwardMode.NO_FF)
@@ -125,6 +128,10 @@ protected void run() throws Exception {
 			mergeCmd.include(srcRef);
 		else
 			mergeCmd.include(src);
+
+		if (message != null)
+			mergeCmd.setMessage(message);
+
 		MergeResult result;
 		try {
 			result = mergeCmd.call();
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 1eeb9f7..761aafa 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
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
- * Copyright (C) 2010-2012, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010-2014, Christian Halstrick <christian.halstrick@sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -1566,6 +1566,69 @@ public void testRecursiveMergeWithConflict() throws Exception {
 		assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
 	}
 
+	@Test
+	public void testMergeWithMessageOption() throws Exception {
+		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("b", "1\nb\n3\n");
+		git.add().addFilepattern("b").call();
+		git.commit().setMessage("side").call();
+
+		checkoutBranch("refs/heads/master");
+
+		writeTrashFile("c", "1\nc\n3\n");
+		git.add().addFilepattern("c").call();
+		git.commit().setMessage("main").call();
+
+		Ref sideBranch = db.getRef("side");
+
+		git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE)
+				.setMessage("user message").call();
+
+		assertNull(db.readMergeCommitMsg());
+
+		Iterator<RevCommit> it = git.log().call().iterator();
+		RevCommit newHead = it.next();
+		assertEquals("user message", newHead.getFullMessage());
+	}
+
+	@Test
+	public void testMergeConflictWithMessageOption() throws Exception {
+		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();
+		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();
+
+		Ref sideBranch = db.getRef("side");
+
+		git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE)
+				.setMessage("user message").call();
+
+		assertEquals("user message\n\nConflicts:\n\ta\n",
+				db.readMergeCommitMsg());
+	}
+
 	private static void setExecutable(Git git, String path, boolean executable) {
 		FS.DETECTED.setExecute(
 				new File(git.getRepository().getWorkTree(), path), executable);
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 6f0313d..0488a41 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * Copyright (C) 2010-2012, Stefan Lay <stefan.lay@sap.com>
+ * Copyright (C) 2010-2014, Stefan Lay <stefan.lay@sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -104,6 +104,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
 
 	private FastForwardMode fastForwardMode;
 
+	private String message;
+
 	/**
 	 * The modes available for fast forward merges corresponding to the
 	 * <code>--ff</code>, <code>--no-ff</code> and <code>--ff-only</code>
@@ -313,7 +315,10 @@ public MergeResult call() throws GitAPIException, NoHeadException,
 				}
 				String mergeMessage = ""; //$NON-NLS-1$
 				if (!squash) {
-					mergeMessage = new MergeMessageFormatter().format(
+					if (message != null)
+						mergeMessage = message;
+					else
+						mergeMessage = new MergeMessageFormatter().format(
 							commits, head);
 					repo.writeMergeCommitMsg(mergeMessage);
 					repo.writeMergeHeads(Arrays.asList(ref.getObjectId()));
@@ -565,4 +570,18 @@ public MergeCommand setCommit(boolean commit) {
 		this.commit = Boolean.valueOf(commit);
 		return this;
 	}
+
+	/**
+	 * Set the commit message to be used for the merge commit (in case one is
+	 * created)
+	 *
+	 * @param message
+	 *            the message to be used for the merge commit
+	 * @return {@code this}
+	 * @since 3.5
+	 */
+	public MergeCommand setMessage(String message) {
+		this.message = message;
+		return this;
+	}
 }