Browse Source

Add "--squash" option to MergeCommand

CQ: 6570
Bug: 351806
Change-Id: I5e47810376419264ecf4247b5a333af5c8945080
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-2.1
Tomasz Zarna 13 years ago committed by Matthias Sohn
parent
commit
2656ac1b5a
  1. 43
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
  2. 189
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
  3. 34
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
  4. 4
      org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
  5. 77
      org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java
  6. 89
      org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
  7. 1
      org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
  8. 15
      org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
  9. 90
      org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
  10. 30
      org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
  11. 4
      org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
  12. 1
      org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
  13. 5
      org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
  14. 92
      org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
  15. 4
      org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
  16. 106
      org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java
  17. 41
      org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java

43
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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * 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.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.File; import java.io.File;
@ -68,7 +69,7 @@ import org.eclipse.jgit.util.FS;
import org.junit.Test; import org.junit.Test;
/** /**
* Unit tests of {@link CommitCommand} * Unit tests of {@link CommitCommand}.
*/ */
public class CommitCommandTest extends RepositoryTestCase { public class CommitCommandTest extends RepositoryTestCase {
@ -365,4 +366,42 @@ public class CommitCommandTest extends RepositoryTestCase {
assertEquals(file1Size, cache.getEntry("file1.txt").getLength()); assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
assertEquals(0, cache.getEntry("file2.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());
}
} }

189
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, 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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * 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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; 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.revwalk.RevCommit;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils; 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.Test;
import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theories;
@ -74,6 +78,15 @@ public class MergeCommandTest extends RepositoryTestCase {
public static @DataPoints public static @DataPoints
MergeStrategy[] mergeStrategies = MergeStrategy.get(); MergeStrategy[] mergeStrategies = MergeStrategy.get();
private GitDateFormatter dateFormatter;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
dateFormatter = new GitDateFormatter(Format.DEFAULT);
}
@Test @Test
public void testMergeInItself() throws Exception { public void testMergeInItself() throws Exception {
Git git = new Git(db); Git git = new Git(db);
@ -1096,6 +1109,180 @@ public class MergeCommandTest extends RepositoryTestCase {
assertFalse(canExecute(git, "mergeableButDirty")); 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) { private void setExecutable(Git git, String path, boolean executable) {
FS.DETECTED.setExecute( FS.DETECTED.setExecute(
new File(git.getRepository().getWorkTree(), path), executable); new File(git.getRepository().getWorkTree(), path), executable);

34
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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * 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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -359,6 +360,37 @@ public class ResetCommandTest extends RepositoryTestCase {
assertTrue(head.equals(secondCommit)); 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) private void assertReflog(ObjectId prevHead, ObjectId head)
throws IOException { throws IOException {
// Check the reflog for HEAD // Check the reflog for HEAD

4
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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * 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().size(), 2);
assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId()); assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId());
assertEquals(db.readMergeHeads().get(1), ObjectId.fromString(sampleId)); 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(read(new File(db.getDirectory(), "MERGE_HEAD")), "");
assertEquals(db.readMergeHeads(), null); assertEquals(db.readMergeHeads(), null);
fos = new FileOutputStream(new File(db.getDirectory(), fos = new FileOutputStream(new File(db.getDirectory(),

77
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);
}
}

89
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);
}
}

1
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. sourceIsNotAWildcard=Source is not a wildcard.
sourceRefDoesntResolveToAnyObject=Source ref {0} doesn't resolve to any object. sourceRefDoesntResolveToAnyObject=Source ref {0} doesn't resolve to any object.
sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0} sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0}
squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD
staleRevFlagsOn=Stale RevFlags on {0} staleRevFlagsOn=Stale RevFlags on {0}
startingReadStageWithoutWrittenRequestDataPendingIsNotSupported=Starting read stage without written request data pending is not supported startingReadStageWithoutWrittenRequestDataPendingIsNotSupported=Starting read stage without written request data pending is not supported
stashApplyFailed=Applying stashed changes did not successfully complete stashApplyFailed=Applying stashed changes did not successfully complete

15
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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -488,9 +488,20 @@ public class CommitCommand extends GitCommand<RevCommit> {
Constants.MERGE_MSG, e), e); 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) 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 // an explicit message
throw new NoMessageException(JGitText.get().commitMessageNotSpecified); throw new NoMessageException(JGitText.get().commitMessageNotSpecified);
} }

90
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, 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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * 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.Merger;
import org.eclipse.jgit.merge.ResolveMerger; import org.eclipse.jgit.merge.ResolveMerger;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
import org.eclipse.jgit.merge.SquashMessageFormatter;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.RevWalkUtils;
import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.FileTreeIterator;
/** /**
@ -95,6 +97,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
private List<Ref> commits = new LinkedList<Ref>(); private List<Ref> commits = new LinkedList<Ref>();
private boolean squash;
/** /**
* @param repo * @param repo
*/ */
@ -184,18 +188,41 @@ public class MergeCommand extends GitCommand<MergeResult> {
srcCommit.getTree()); srcCommit.getTree());
dco.setFailOnConflict(true); dco.setFailOnConflict(true);
dco.checkout(); dco.checkout();
String msg = null;
updateHead(refLogMessage, srcCommit, headId); 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); setCallable(false);
return new MergeResult(srcCommit, srcCommit, new ObjectId[] { return new MergeResult(newHead, base, new ObjectId[] {
headCommit, srcCommit }, MergeStatus.FAST_FORWARD, headCommit, srcCommit }, mergeStatus, mergeStrategy,
mergeStrategy, null, null); null, msg);
} else { } else {
String mergeMessage = "";
String mergeMessage = new MergeMessageFormatter().format( if (!squash) {
commits, head); mergeMessage = new MergeMessageFormatter().format(
repo.writeMergeCommitMsg(mergeMessage); commits, head);
repo.writeMergeHeads(Arrays.asList(ref.getObjectId())); 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); Merger merger = mergeStrategy.newMerger(repo);
boolean noProblems; boolean noProblems;
Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults = null; Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults = null;
@ -223,12 +250,22 @@ public class MergeCommand extends GitCommand<MergeResult> {
dco.setFailOnConflict(true); dco.setFailOnConflict(true);
dco.checkout(); 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(); .setReflogComment(refLogMessage.toString()).call();
return new MergeResult(newHead.getId(), mergeStatus = MergeStatus.MERGED;
null, new ObjectId[] { } else {
headCommit.getId(), srcCommit.getId() }, msg = JGitText.get().squashCommitNotUpdatingHEAD;
MergeStatus.MERGED, mergeStrategy, null, null); newHead = headCommit;
mergeStatus = MergeStatus.MERGED_SQUASHED;
}
return new MergeResult(newHead.getId(), null,
new ObjectId[] { headCommit.getId(),
srcCommit.getId() }, mergeStatus,
mergeStrategy, null, msg);
} else { } else {
if (failingPaths != null) { if (failingPaths != null) {
repo.writeMergeCommitMsg(null); repo.writeMergeCommitMsg(null);
@ -334,4 +371,25 @@ public class MergeCommand extends GitCommand<MergeResult> {
return include(new ObjectIdRef.Unpeeled(Storage.LOOSE, name, return include(new ObjectIdRef.Unpeeled(Storage.LOOSE, name,
commit.copy())); 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;
}
} }

30
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, 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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -76,6 +76,20 @@ public class MergeResult {
return true; 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 { ALREADY_UP_TO_DATE {
@Override @Override
@ -112,6 +126,20 @@ public class MergeResult {
return true; return true;
} }
}, },
/**
* @since 2.0
*/
MERGED_SQUASHED {
@Override
public String toString() {
return "Merged-squashed";
}
@Override
public boolean isSuccessful() {
return true;
}
},
/** */ /** */
CONFLICTING { CONFLICTING {
@Override @Override

4
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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -221,6 +221,8 @@ public class ResetCommand extends GitCommand<Ref> {
resetMerge(); resetMerge();
else if (cherryPicking) else if (cherryPicking)
resetCherryPick(); resetCherryPick();
else if (repo.readSquashCommitMsg() != null)
repo.writeSquashCommitMsg(null /* delete */);
} }
setCallable(false); setCallable(false);

1
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java

@ -484,6 +484,7 @@ public class JGitText extends TranslationBundle {
/***/ public String sourceIsNotAWildcard; /***/ public String sourceIsNotAWildcard;
/***/ public String sourceRefDoesntResolveToAnyObject; /***/ public String sourceRefDoesntResolveToAnyObject;
/***/ public String sourceRefNotSpecifiedForRefspec; /***/ public String sourceRefNotSpecifiedForRefspec;
/***/ public String squashCommitNotUpdatingHEAD;
/***/ public String staleRevFlagsOn; /***/ public String staleRevFlagsOn;
/***/ public String startingReadStageWithoutWrittenRequestDataPendingIsNotSupported; /***/ public String startingReadStageWithoutWrittenRequestDataPendingIsNotSupported;
/***/ public String stashApplyFailed; /***/ public String stashApplyFailed;

5
org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java

@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2008, Google Inc. * Copyright (C) 2008, Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> * 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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * 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 */ /** 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"; 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 * name of the ref ORIG_HEAD used by certain commands to store the original
* value of HEAD * value of HEAD

92
org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java

@ -2,7 +2,7 @@
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2008-2010, Google Inc. * Copyright (C) 2008-2010, Google Inc.
* Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com> * 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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -1125,24 +1125,14 @@ public abstract class Repository {
* See {@link #isBare()}. * See {@link #isBare()}.
*/ */
public String readMergeCommitMsg() throws IOException, NoWorkTreeException { public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null) return readCommitMsgFile(Constants.MERGE_MSG);
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;
}
} }
/** /**
* Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations * 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 * 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 * merge commit. If <code>null</code> is specified as message the file will
* be deleted * be deleted.
* *
* @param msg * @param msg
* the message which should be written or <code>null</code> to * 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 { public void writeMergeCommitMsg(String msg) throws IOException {
File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG); File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
if (msg != null) { writeCommitMsg(mergeMsgFile, msg);
FileOutputStream fos = new FileOutputStream(mergeMsgFile);
try {
fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
} finally {
fos.close();
}
} else {
FileUtils.delete(mergeMsgFile, FileUtils.SKIP_MISSING);
}
} }
/** /**
@ -1169,9 +1150,9 @@ public abstract class Repository {
* file operations triggering a merge will store the IDs of all heads which * file operations triggering a merge will store the IDs of all heads which
* should be merged together with HEAD. * should be merged together with HEAD.
* *
* @return a list of commits which IDs are listed in the MERGE_HEAD * @return a list of commits which IDs are listed in the MERGE_HEAD file or
* file or {@code null} if this file doesn't exist. Also if the file * {@code null} if this file doesn't exist. Also if the file exists
* exists but is empty {@code null} will be returned * but is empty {@code null} will be returned
* @throws IOException * @throws IOException
* @throws NoWorkTreeException * @throws NoWorkTreeException
* if this is bare, which implies it has no working directory. * if this is bare, which implies it has no working directory.
@ -1280,6 +1261,65 @@ public abstract class Repository {
return raw != null ? ObjectId.fromString(raw, 0) : null; return raw != null ? ObjectId.fromString(raw, 0) : null;
} }
/**
* 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. * Read a file from the git directory.
* *

4
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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -134,7 +134,7 @@ public class MergeMessageFormatter {
public String formatWithConflicts(String message, public String formatWithConflicts(String message,
List<String> conflictingPaths) { List<String> conflictingPaths) {
StringBuilder sb = new StringBuilder(message); StringBuilder sb = new StringBuilder(message);
if (!message.endsWith("\n")) if (!message.endsWith("\n") && message.length() != 0)
sb.append("\n"); sb.append("\n");
sb.append("\n"); sb.append("\n");
sb.append("Conflicts:\n"); sb.append("Conflicts:\n");

106
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();
}
}

41
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. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -44,6 +44,8 @@
package org.eclipse.jgit.revwalk; package org.eclipse.jgit.revwalk;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
@ -83,14 +85,43 @@ public final class RevWalkUtils {
public static int count(final RevWalk walk, final RevCommit start, public static int count(final RevWalk walk, final RevCommit start,
final RevCommit end) throws MissingObjectException, final RevCommit end) throws MissingObjectException,
IncorrectObjectTypeException, IOException { 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.reset();
walk.markStart(start); walk.markStart(start);
if (end != null) if (end != null)
walk.markUninteresting(end); walk.markUninteresting(end);
int count = 0; List<RevCommit> commits = new ArrayList<RevCommit>();
for (RevCommit c = walk.next(); c != null; c = walk.next()) for (RevCommit c : walk)
count++; commits.add(c);
return count; return commits;
} }
} }

Loading…
Cancel
Save