aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Zarna2012-06-13 17:28:55 (EDT)
committerMatthias Sohn2012-06-15 02:59:41 (EDT)
commit2656ac1b5acb9b73c6b47e2cf8830a0a0b2cc214 (patch)
tree1f48ca5fafef4ae7482c88b2084d532508bb944c
parentc4087af65ddfd976f2a2513a773f50b1fd790336 (diff)
downloadjgit-2656ac1b5acb9b73c6b47e2cf8830a0a0b2cc214.zip
jgit-2656ac1b5acb9b73c6b47e2cf8830a0a0b2cc214.tar.gz
jgit-2656ac1b5acb9b73c6b47e2cf8830a0a0b2cc214.tar.bz2
Add "--squash" option to MergeCommandrefs/changes/95/4995/12
CQ: 6570 Bug: 351806 Change-Id: I5e47810376419264ecf4247b5a333af5c8945080 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java43
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java189
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java34
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java77
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java89
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java90
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java30
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java106
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java41
17 files changed, 765 insertions, 60 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index 3aec611..9b597d3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, GitHub Inc.
+ * Copyright (C) 2011-2012, GitHub Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,6 +44,7 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
@@ -68,7 +69,7 @@ import org.eclipse.jgit.util.FS;
import org.junit.Test;
/**
- * Unit tests of {@link CommitCommand}
+ * Unit tests of {@link CommitCommand}.
*/
public class CommitCommandTest extends RepositoryTestCase {
@@ -365,4 +366,42 @@ public class CommitCommandTest extends RepositoryTestCase {
assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
assertEquals(0, cache.getEntry("file2.txt").getLength());
}
+
+ @Test
+ public void commitAfterSquashMerge() throws Exception {
+ Git git = new Git(db);
+
+ writeTrashFile("file1", "file1");
+ git.add().addFilepattern("file1").call();
+ RevCommit first = git.commit().setMessage("initial commit").call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ createBranch(first, "refs/heads/branch1");
+ checkoutBranch("refs/heads/branch1");
+
+ writeTrashFile("file2", "file2");
+ git.add().addFilepattern("file2").call();
+ git.commit().setMessage("second commit").call();
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ checkoutBranch("refs/heads/master");
+
+ MergeResult result = git.merge().include(db.getRef("branch1"))
+ .setSquash(true).call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+ assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
+ result.getMergeStatus());
+
+ // comment not set, should be inferred from SQUASH_MSG
+ RevCommit squashedCommit = git.commit().call();
+
+ assertEquals(1, squashedCommit.getParentCount());
+ assertNull(db.readSquashCommitMsg());
+ assertEquals("commit: Squashed commit of the following:", db
+ .getReflogReader(Constants.HEAD).getLastEntry().getComment());
+ assertEquals("commit: Squashed commit of the following:", db
+ .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ }
}
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 30a9452..c6875b4 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, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010-2012, 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
@@ -45,6 +45,7 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -62,6 +63,9 @@ import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.GitDateFormatter;
+import org.eclipse.jgit.util.GitDateFormatter.Format;
+import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
@@ -74,6 +78,15 @@ public class MergeCommandTest extends RepositoryTestCase {
public static @DataPoints
MergeStrategy[] mergeStrategies = MergeStrategy.get();
+ private GitDateFormatter dateFormatter;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ dateFormatter = new GitDateFormatter(Format.DEFAULT);
+ }
+
@Test
public void testMergeInItself() throws Exception {
Git git = new Git(db);
@@ -1096,6 +1109,180 @@ public class MergeCommandTest extends RepositoryTestCase {
assertFalse(canExecute(git, "mergeableButDirty"));
}
+ @Test
+ public void testSquashFastForward() throws Exception {
+ Git git = new Git(db);
+
+ writeTrashFile("file1", "file1");
+ git.add().addFilepattern("file1").call();
+ RevCommit first = git.commit().setMessage("initial commit").call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ createBranch(first, "refs/heads/branch1");
+ checkoutBranch("refs/heads/branch1");
+
+ writeTrashFile("file2", "file2");
+ git.add().addFilepattern("file2").call();
+ RevCommit second = git.commit().setMessage("second commit").call();
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ writeTrashFile("file3", "file3");
+ git.add().addFilepattern("file3").call();
+ RevCommit third = git.commit().setMessage("third commit").call();
+ assertTrue(new File(db.getWorkTree(), "file3").exists());
+
+ checkoutBranch("refs/heads/master");
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertFalse(new File(db.getWorkTree(), "file2").exists());
+ assertFalse(new File(db.getWorkTree(), "file3").exists());
+
+ MergeResult result = git.merge().include(db.getRef("branch1"))
+ .setSquash(true).call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+ assertTrue(new File(db.getWorkTree(), "file3").exists());
+ assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
+ result.getMergeStatus());
+ assertEquals(first, result.getNewHead()); // HEAD didn't move
+ assertEquals(first, db.resolve(Constants.HEAD + "^{commit}"));
+
+ assertEquals(
+ "Squashed commit of the following:\n\ncommit "
+ + third.getName()
+ + "\nAuthor: "
+ + third.getAuthorIdent().getName()
+ + " <"
+ + third.getAuthorIdent().getEmailAddress()
+ + ">\nDate: "
+ + dateFormatter.formatDate(third
+ .getAuthorIdent())
+ + "\n\n\tthird commit\n\ncommit "
+ + second.getName()
+ + "\nAuthor: "
+ + second.getAuthorIdent().getName()
+ + " <"
+ + second.getAuthorIdent().getEmailAddress()
+ + ">\nDate: "
+ + dateFormatter.formatDate(second
+ .getAuthorIdent()) + "\n\n\tsecond commit\n",
+ db.readSquashCommitMsg());
+ assertNull(db.readMergeCommitMsg());
+
+ Status stat = git.status().call();
+ assertEquals(StatusCommandTest.set("file2", "file3"), stat.getAdded());
+ }
+
+ @Test
+ public void testSquashMerge() throws Exception {
+ Git git = new Git(db);
+
+ writeTrashFile("file1", "file1");
+ git.add().addFilepattern("file1").call();
+ RevCommit first = git.commit().setMessage("initial commit").call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ createBranch(first, "refs/heads/branch1");
+
+ writeTrashFile("file2", "file2");
+ git.add().addFilepattern("file2").call();
+ RevCommit second = git.commit().setMessage("second commit").call();
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ checkoutBranch("refs/heads/branch1");
+
+ writeTrashFile("file3", "file3");
+ git.add().addFilepattern("file3").call();
+ RevCommit third = git.commit().setMessage("third commit").call();
+ assertTrue(new File(db.getWorkTree(), "file3").exists());
+
+ checkoutBranch("refs/heads/master");
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+ assertFalse(new File(db.getWorkTree(), "file3").exists());
+
+ MergeResult result = git.merge().include(db.getRef("branch1"))
+ .setSquash(true).call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+ assertTrue(new File(db.getWorkTree(), "file3").exists());
+ assertEquals(MergeResult.MergeStatus.MERGED_SQUASHED,
+ result.getMergeStatus());
+ assertEquals(second, result.getNewHead()); // HEAD didn't move
+ assertEquals(second, db.resolve(Constants.HEAD + "^{commit}"));
+
+ assertEquals(
+ "Squashed commit of the following:\n\ncommit "
+ + third.getName()
+ + "\nAuthor: "
+ + third.getAuthorIdent().getName()
+ + " <"
+ + third.getAuthorIdent().getEmailAddress()
+ + ">\nDate: "
+ + dateFormatter.formatDate(third
+ .getAuthorIdent()) + "\n\n\tthird commit\n",
+ db.readSquashCommitMsg());
+ assertNull(db.readMergeCommitMsg());
+
+ Status stat = git.status().call();
+ assertEquals(StatusCommandTest.set("file3"), stat.getAdded());
+ }
+
+ @Test
+ public void testSquashMergeConflict() throws Exception {
+ Git git = new Git(db);
+
+ writeTrashFile("file1", "file1");
+ git.add().addFilepattern("file1").call();
+ RevCommit first = git.commit().setMessage("initial commit").call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ createBranch(first, "refs/heads/branch1");
+
+ writeTrashFile("file2", "master");
+ git.add().addFilepattern("file2").call();
+ RevCommit second = git.commit().setMessage("second commit").call();
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ checkoutBranch("refs/heads/branch1");
+
+ writeTrashFile("file2", "branch");
+ git.add().addFilepattern("file2").call();
+ RevCommit third = git.commit().setMessage("third commit").call();
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ checkoutBranch("refs/heads/master");
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ MergeResult result = git.merge().include(db.getRef("branch1"))
+ .setSquash(true).call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+ assertEquals(MergeResult.MergeStatus.CONFLICTING,
+ result.getMergeStatus());
+ assertNull(result.getNewHead());
+ assertEquals(second, db.resolve(Constants.HEAD + "^{commit}"));
+
+ assertEquals(
+ "Squashed commit of the following:\n\ncommit "
+ + third.getName()
+ + "\nAuthor: "
+ + third.getAuthorIdent().getName()
+ + " <"
+ + third.getAuthorIdent().getEmailAddress()
+ + ">\nDate: "
+ + dateFormatter.formatDate(third
+ .getAuthorIdent()) + "\n\n\tthird commit\n",
+ db.readSquashCommitMsg());
+ assertEquals("\nConflicts:\n\tfile2\n", db.readMergeCommitMsg());
+
+ Status stat = git.status().call();
+ assertEquals(StatusCommandTest.set("file2"), stat.getConflicting());
+ }
+
private void setExecutable(Git git, String path, boolean executable) {
FS.DETECTED.setExecute(
new File(git.getRepository().getWorkTree(), path), executable);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
index 0806cf0..c55775c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Chris Aniszczyk <caniszczyk@gmail.com>
+ * Copyright (C) 2011-2012, Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -46,6 +46,7 @@ import static org.eclipse.jgit.api.ResetCommand.ResetType.HARD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -359,6 +360,37 @@ public class ResetCommandTest extends RepositoryTestCase {
assertTrue(head.equals(secondCommit));
}
+ @Test
+ public void testHardResetAfterSquashMerge() throws Exception {
+ Git g = new Git(db);
+
+ writeTrashFile("file1", "file1");
+ g.add().addFilepattern("file1").call();
+ RevCommit first = g.commit().setMessage("initial commit").call();
+
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ createBranch(first, "refs/heads/branch1");
+ checkoutBranch("refs/heads/branch1");
+
+ writeTrashFile("file2", "file2");
+ g.add().addFilepattern("file2").call();
+ g.commit().setMessage("second commit").call();
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ checkoutBranch("refs/heads/master");
+
+ MergeResult result = g.merge().include(db.getRef("branch1"))
+ .setSquash(true).call();
+
+ assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
+ result.getMergeStatus());
+ assertNotNull(db.readSquashCommitMsg());
+
+ g.reset().setMode(ResetType.HARD).setRef(first.getName()).call();
+
+ assertNull(db.readSquashCommitMsg());
+ }
+
private void assertReflog(ObjectId prevHead, ObjectId head)
throws IOException {
// Check the reflog for HEAD
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
index 4e128e7..ed1f967 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010-2012 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
@@ -78,7 +78,7 @@ public class MergeHeadMsgTest extends RepositoryTestCase {
assertEquals(db.readMergeHeads().size(), 2);
assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId());
assertEquals(db.readMergeHeads().get(1), ObjectId.fromString(sampleId));
- db.writeMergeHeads(Collections.EMPTY_LIST);
+ db.writeMergeHeads(Collections.<ObjectId> emptyList());
assertEquals(read(new File(db.getDirectory(), "MERGE_HEAD")), "");
assertEquals(db.readMergeHeads(), null);
fos = new FileOutputStream(new File(db.getDirectory(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java
new file mode 100644
index 0000000..4374e47
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012, 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
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+
+public class SquashCommitMsgTest extends RepositoryTestCase {
+ private static final String squashMsg = "squashed commit";
+
+ @Test
+ public void testReadWriteMergeMsg() throws IOException {
+ assertEquals(db.readSquashCommitMsg(), null);
+ assertFalse(new File(db.getDirectory(), Constants.SQUASH_MSG).exists());
+ db.writeSquashCommitMsg(squashMsg);
+ assertEquals(squashMsg, db.readSquashCommitMsg());
+ assertEquals(read(new File(db.getDirectory(), Constants.SQUASH_MSG)),
+ squashMsg);
+ db.writeSquashCommitMsg(null);
+ assertEquals(db.readSquashCommitMsg(), null);
+ assertFalse(new File(db.getDirectory(), Constants.SQUASH_MSG).exists());
+ FileOutputStream fos = new FileOutputStream(new File(db.getDirectory(),
+ Constants.SQUASH_MSG));
+ try {
+ fos.write(squashMsg.getBytes(Constants.CHARACTER_ENCODING));
+ } finally {
+ fos.close();
+ }
+ assertEquals(db.readSquashCommitMsg(), squashMsg);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
new file mode 100644
index 0000000..4274d8b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012, 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
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.merge;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.GitDateFormatter;
+import org.eclipse.jgit.util.GitDateFormatter.Format;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test construction of squash message by {@link SquashMessageFormatterTest}.
+ */
+public class SquashMessageFormatterTest extends SampleDataRepositoryTestCase {
+ private GitDateFormatter dateFormatter;
+ private SquashMessageFormatter msgFormatter;
+ private RevCommit revCommit;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ dateFormatter = new GitDateFormatter(Format.DEFAULT);
+ msgFormatter = new SquashMessageFormatter();
+ }
+
+ @Test
+ public void testCommit() throws Exception {
+ Git git = new Git(db);
+ revCommit = git.commit().setMessage("squash_me").call();
+
+ Ref master = db.getRef("refs/heads/master");
+ String message = msgFormatter.format(Arrays.asList(revCommit), master);
+ assertEquals(
+ "Squashed commit of the following:\n\ncommit "
+ + revCommit.getName() + "\nAuthor: "
+ + revCommit.getAuthorIdent().getName() + " <"
+ + revCommit.getAuthorIdent().getEmailAddress()
+ + ">\nDate: " + dateFormatter.formatDate(author)
+ + "\n\n\tsquash_me\n", message);
+ }
+}
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index be60802..1131c15 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -424,6 +424,7 @@ sourceDestinationMustMatch=Source/Destination must match.
sourceIsNotAWildcard=Source is not a wildcard.
sourceRefDoesntResolveToAnyObject=Source ref {0} doesn't resolve to any object.
sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0}
+squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD
staleRevFlagsOn=Stale RevFlags on {0}
startingReadStageWithoutWrittenRequestDataPendingIsNotSupported=Starting read stage without written request data pending is not supported
stashApplyFailed=Applying stashed changes did not successfully complete
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index eac6fe6..ae6d629 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010-2012, 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
@@ -488,9 +488,20 @@ public class CommitCommand extends GitCommand<RevCommit> {
Constants.MERGE_MSG, e), e);
}
}
+ } else if (state == RepositoryState.SAFE && message == null) {
+ try {
+ message = repo.readSquashCommitMsg();
+ if (message != null)
+ repo.writeSquashCommitMsg(null /* delete */);
+ } catch (IOException e) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().exceptionOccurredDuringReadingOfGIT_DIR,
+ Constants.MERGE_MSG, e), e);
+ }
+
}
if (message == null)
- // as long as we don't suppport -C option we have to have
+ // as long as we don't support -C option we have to have
// an explicit message
throw new NoMessageException(JGitText.get().commitMessageNotSpecified);
}
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 c5a9552..3ca861c 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, Stefan Lay <stefan.lay@sap.com>
+ * Copyright (C) 2010-2012, 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
@@ -76,8 +76,10 @@ import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.Merger;
import org.eclipse.jgit.merge.ResolveMerger;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
+import org.eclipse.jgit.merge.SquashMessageFormatter;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.revwalk.RevWalkUtils;
import org.eclipse.jgit.treewalk.FileTreeIterator;
/**
@@ -95,6 +97,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
private List<Ref> commits = new LinkedList<Ref>();
+ private boolean squash;
+
/**
* @param repo
*/
@@ -184,18 +188,41 @@ public class MergeCommand extends GitCommand<MergeResult> {
srcCommit.getTree());
dco.setFailOnConflict(true);
dco.checkout();
-
- updateHead(refLogMessage, srcCommit, headId);
+ String msg = null;
+ ObjectId newHead, base = null;
+ MergeStatus mergeStatus = null;
+ if (!squash) {
+ updateHead(refLogMessage, srcCommit, headId);
+ newHead = base = srcCommit;
+ mergeStatus = MergeStatus.FAST_FORWARD;
+ } else {
+ msg = JGitText.get().squashCommitNotUpdatingHEAD;
+ newHead = base = headId;
+ mergeStatus = MergeStatus.FAST_FORWARD_SQUASHED;
+ List<RevCommit> squashedCommits = RevWalkUtils.find(
+ revWalk, srcCommit, headCommit);
+ String squashMessage = new SquashMessageFormatter().format(
+ squashedCommits, head);
+ repo.writeSquashCommitMsg(squashMessage);
+ }
setCallable(false);
- return new MergeResult(srcCommit, srcCommit, new ObjectId[] {
- headCommit, srcCommit }, MergeStatus.FAST_FORWARD,
- mergeStrategy, null, null);
+ return new MergeResult(newHead, base, new ObjectId[] {
+ headCommit, srcCommit }, mergeStatus, mergeStrategy,
+ null, msg);
} else {
-
- String mergeMessage = new MergeMessageFormatter().format(
- commits, head);
- repo.writeMergeCommitMsg(mergeMessage);
- repo.writeMergeHeads(Arrays.asList(ref.getObjectId()));
+ String mergeMessage = "";
+ if (!squash) {
+ mergeMessage = new MergeMessageFormatter().format(
+ commits, head);
+ repo.writeMergeCommitMsg(mergeMessage);
+ repo.writeMergeHeads(Arrays.asList(ref.getObjectId()));
+ } else {
+ List<RevCommit> squashedCommits = RevWalkUtils.find(
+ revWalk, srcCommit, headCommit);
+ String squashMessage = new SquashMessageFormatter().format(
+ squashedCommits, head);
+ repo.writeSquashCommitMsg(squashMessage);
+ }
Merger merger = mergeStrategy.newMerger(repo);
boolean noProblems;
Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults = null;
@@ -223,12 +250,22 @@ public class MergeCommand extends GitCommand<MergeResult> {
dco.setFailOnConflict(true);
dco.checkout();
- RevCommit newHead = new Git(getRepository()).commit()
+ String msg = null;
+ RevCommit newHead = null;
+ MergeStatus mergeStatus = null;
+ if (!squash) {
+ newHead = new Git(getRepository()).commit()
.setReflogComment(refLogMessage.toString()).call();
- return new MergeResult(newHead.getId(),
- null, new ObjectId[] {
- headCommit.getId(), srcCommit.getId() },
- MergeStatus.MERGED, mergeStrategy, null, null);
+ mergeStatus = MergeStatus.MERGED;
+ } else {
+ msg = JGitText.get().squashCommitNotUpdatingHEAD;
+ newHead = headCommit;
+ mergeStatus = MergeStatus.MERGED_SQUASHED;
+ }
+ return new MergeResult(newHead.getId(), null,
+ new ObjectId[] { headCommit.getId(),
+ srcCommit.getId() }, mergeStatus,
+ mergeStrategy, null, msg);
} else {
if (failingPaths != null) {
repo.writeMergeCommitMsg(null);
@@ -334,4 +371,25 @@ public class MergeCommand extends GitCommand<MergeResult> {
return include(new ObjectIdRef.Unpeeled(Storage.LOOSE, name,
commit.copy()));
}
+
+ /**
+ * If <code>true</code>, will prepare the next commit in working tree and
+ * index as if a real merge happened, but do not make the commit or move the
+ * HEAD. Otherwise, perform the merge and commit the result.
+ * <p>
+ * In case the merge was successful but this flag was set to
+ * <code>true</code> a {@link MergeResult} with status
+ * {@link MergeStatus#MERGED_SQUASHED} or
+ * {@link MergeStatus#FAST_FORWARD_SQUASHED} is returned.
+ *
+ * @param squash
+ * whether to squash commits or not
+ * @return {@code this}
+ * @since 2.0
+ */
+ public MergeCommand setSquash(boolean squash) {
+ checkCallable();
+ this.squash = squash;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
index c1733f5..484039e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010-2012, 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
@@ -76,6 +76,20 @@ public class MergeResult {
return true;
}
},
+ /**
+ * @since 2.0
+ */
+ FAST_FORWARD_SQUASHED {
+ @Override
+ public String toString() {
+ return "Fast-forward-squashed";
+ }
+
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
+ },
/** */
ALREADY_UP_TO_DATE {
@Override
@@ -112,6 +126,20 @@ public class MergeResult {
return true;
}
},
+ /**
+ * @since 2.0
+ */
+ MERGED_SQUASHED {
+ @Override
+ public String toString() {
+ return "Merged-squashed";
+ }
+
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
+ },
/** */
CONFLICTING {
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
index b34b902..fc3d147 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Chris Aniszczyk <caniszczyk@gmail.com>
+ * Copyright (C) 2011-2012, Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -221,6 +221,8 @@ public class ResetCommand extends GitCommand<Ref> {
resetMerge();
else if (cherryPicking)
resetCherryPick();
+ else if (repo.readSquashCommitMsg() != null)
+ repo.writeSquashCommitMsg(null /* delete */);
}
setCallable(false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index aca9574..539f837 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -484,6 +484,7 @@ public class JGitText extends TranslationBundle {
/***/ public String sourceIsNotAWildcard;
/***/ public String sourceRefDoesntResolveToAnyObject;
/***/ public String sourceRefNotSpecifiedForRefspec;
+ /***/ public String squashCommitNotUpdatingHEAD;
/***/ public String staleRevFlagsOn;
/***/ public String startingReadStageWithoutWrittenRequestDataPendingIsNotSupported;
/***/ public String stashApplyFailed;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 5332ffa..d5be315 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2008, Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -553,6 +553,9 @@ public final class Constants {
/** name of the file containing the ID of a cherry pick commit in case of conflicts */
public static final String CHERRY_PICK_HEAD = "CHERRY_PICK_HEAD";
+ /** name of the file containing the commit msg for a squash commit */
+ public static final String SQUASH_MSG = "SQUASH_MSG";
+
/**
* name of the ref ORIG_HEAD used by certain commands to store the original
* value of HEAD
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 7b9d453..2c04a74 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -2,7 +2,7 @@
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2008-2010, Google Inc.
* Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -1125,24 +1125,14 @@ public abstract class Repository {
* See {@link #isBare()}.
*/
public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
- if (isBare() || getDirectory() == null)
- throw new NoWorkTreeException();
-
- File mergeMsgFile = new File(getDirectory(), Constants.MERGE_MSG);
- try {
- return RawParseUtils.decode(IO.readFully(mergeMsgFile));
- } catch (FileNotFoundException e) {
- // MERGE_MSG file has disappeared in the meantime
- // ignore it
- return null;
- }
+ return readCommitMsgFile(Constants.MERGE_MSG);
}
/**
* Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
* triggering a merge will store a template for the commit message of the
* merge commit. If <code>null</code> is specified as message the file will
- * be deleted
+ * be deleted.
*
* @param msg
* the message which should be written or <code>null</code> to
@@ -1152,16 +1142,7 @@ public abstract class Repository {
*/
public void writeMergeCommitMsg(String msg) throws IOException {
File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
- if (msg != null) {
- FileOutputStream fos = new FileOutputStream(mergeMsgFile);
- try {
- fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
- } finally {
- fos.close();
- }
- } else {
- FileUtils.delete(mergeMsgFile, FileUtils.SKIP_MISSING);
- }
+ writeCommitMsg(mergeMsgFile, msg);
}
/**
@@ -1169,9 +1150,9 @@ public abstract class Repository {
* file operations triggering a merge will store the IDs of all heads which
* should be merged together with HEAD.
*
- * @return a list of commits which IDs are listed in the MERGE_HEAD
- * file or {@code null} if this file doesn't exist. Also if the file
- * exists but is empty {@code null} will be returned
+ * @return a list of commits which IDs are listed in the MERGE_HEAD file or
+ * {@code null} if this file doesn't exist. Also if the file exists
+ * but is empty {@code null} will be returned
* @throws IOException
* @throws NoWorkTreeException
* if this is bare, which implies it has no working directory.
@@ -1281,6 +1262,65 @@ public abstract class Repository {
}
/**
+ * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this
+ * file operations triggering a squashed merge will store a template for the
+ * commit message of the squash commit.
+ *
+ * @return a String containing the content of the SQUASH_MSG file or
+ * {@code null} if this file doesn't exist
+ * @throws IOException
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
+ */
+ public String readSquashCommitMsg() throws IOException {
+ return readCommitMsgFile(Constants.SQUASH_MSG);
+ }
+
+ /**
+ * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file
+ * operations triggering a squashed merge will store a template for the
+ * commit message of the squash commit. If <code>null</code> is specified as
+ * message the file will be deleted.
+ *
+ * @param msg
+ * the message which should be written or <code>null</code> to
+ * delete the file
+ *
+ * @throws IOException
+ */
+ public void writeSquashCommitMsg(String msg) throws IOException {
+ File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
+ writeCommitMsg(squashMsgFile, msg);
+ }
+
+ private String readCommitMsgFile(String msgFilename) throws IOException {
+ if (isBare() || getDirectory() == null)
+ throw new NoWorkTreeException();
+
+ File mergeMsgFile = new File(getDirectory(), msgFilename);
+ try {
+ return RawParseUtils.decode(IO.readFully(mergeMsgFile));
+ } catch (FileNotFoundException e) {
+ // the file has disappeared in the meantime ignore it
+ return null;
+ }
+ }
+
+ private void writeCommitMsg(File msgFile, String msg) throws IOException {
+ if (msg != null) {
+ FileOutputStream fos = new FileOutputStream(msgFile);
+ try {
+ fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
+ } finally {
+ fos.close();
+ }
+ } else {
+ FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
+ }
+ }
+
+ /**
* Read a file from the git directory.
*
* @param filename
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
index 1a26ecf..cacaff4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Robin Stocker <robin@nibor.org>
+ * Copyright (C) 2010-2012, Robin Stocker <robin@nibor.org>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -134,7 +134,7 @@ public class MergeMessageFormatter {
public String formatWithConflicts(String message,
List<String> conflictingPaths) {
StringBuilder sb = new StringBuilder(message);
- if (!message.endsWith("\n"))
+ if (!message.endsWith("\n") && message.length() != 0)
sb.append("\n");
sb.append("\n");
sb.append("Conflicts:\n");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java
new file mode 100644
index 0000000..6d0ff9b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012, 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
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.merge;
+
+import java.util.List;
+
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.GitDateFormatter;
+import org.eclipse.jgit.util.GitDateFormatter.Format;
+
+/**
+ * Formatter for constructing the commit message for a squashed commit.
+ * <p>
+ * The format should be the same as C Git does it, for compatibility.
+ */
+public class SquashMessageFormatter {
+
+ private GitDateFormatter dateFormatter;
+
+ /**
+ * Create a new squash message formatter.
+ */
+ public SquashMessageFormatter() {
+ dateFormatter = new GitDateFormatter(Format.DEFAULT);
+ }
+ /**
+ * Construct the squashed commit message.
+ *
+ * @param squashedCommits
+ * the squashed commits
+ * @param target
+ * the target branch
+ * @return squashed commit message
+ */
+ public String format(List<RevCommit> squashedCommits, Ref target) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Squashed commit of the following:\n");
+ for (RevCommit c : squashedCommits) {
+ sb.append("\ncommit ");
+ sb.append(c.getName());
+ sb.append("\n");
+ sb.append(toString(c.getAuthorIdent()));
+ sb.append("\n\t");
+ sb.append(c.getShortMessage());
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ private String toString(PersonIdent author) {
+ final StringBuilder a = new StringBuilder();
+
+ a.append("Author: ");
+ a.append(author.getName());
+ a.append(" <");
+ a.append(author.getEmailAddress());
+ a.append(">\n");
+ a.append("Date: ");
+ a.append(dateFormatter.formatDate(author));
+ a.append("\n");
+
+ return a.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
index 50b222b..94400b0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Robin Stocker <robin@nibor.org>
+ * Copyright (C) 2011-2012, Robin Stocker <robin@nibor.org>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,6 +44,8 @@
package org.eclipse.jgit.revwalk;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
@@ -83,14 +85,43 @@ public final class RevWalkUtils {
public static int count(final RevWalk walk, final RevCommit start,
final RevCommit end) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
+ return find(walk, start, end).size();
+ }
+
+ /**
+ * Find commits that are reachable from <code>start</code> until a commit
+ * that is reachable from <code>end</code> is encountered. In other words,
+ * Find of commits that are in <code>start</code>, but not in
+ * <code>end</code>.
+ * <p>
+ * Note that this method calls {@link RevWalk#reset()} at the beginning.
+ * Also note that the existing rev filter on the walk is left as-is, so be
+ * sure to set the right rev filter before calling this method.
+ *
+ * @param walk
+ * the rev walk to use
+ * @param start
+ * the commit to start counting from
+ * @param end
+ * the commit where counting should end, or null if counting
+ * should be done until there are no more commits
+ * @return the commits found
+ * @throws MissingObjectException
+ * @throws IncorrectObjectTypeException
+ * @throws IOException
+ */
+ public static List<RevCommit> find(final RevWalk walk,
+ final RevCommit start, final RevCommit end)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
walk.reset();
walk.markStart(start);
if (end != null)
walk.markUninteresting(end);
- int count = 0;
- for (RevCommit c = walk.next(); c != null; c = walk.next())
- count++;
- return count;
+ List<RevCommit> commits = new ArrayList<RevCommit>();
+ for (RevCommit c : walk)
+ commits.add(c);
+ return commits;
}
}