diff options
Diffstat (limited to 'org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java')
-rw-r--r-- | org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java | 963 |
1 files changed, 703 insertions, 260 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java index 8b0ed5f39d..4c8cf06a67 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java @@ -1,47 +1,15 @@ /* - * Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler@sap.com> - * and other copyright owners as documented in the project's IP log. + * Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler@sap.com> and others * - * 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 + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://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. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; @@ -56,12 +24,15 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.time.Instant; +import java.time.ZoneOffset; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.jgit.api.MergeResult.MergeStatus; import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler; +import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler2; import org.eclipse.jgit.api.RebaseCommand.Operation; import org.eclipse.jgit.api.RebaseResult.Status; import org.eclipse.jgit.api.errors.InvalidRebaseStepException; @@ -74,8 +45,11 @@ import org.eclipse.jgit.errors.AmbiguousObjectException; import org.eclipse.jgit.errors.IllegalTodoFileModification; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.events.ChangeRecorder; +import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.AbbreviatedObjectId; +import org.eclipse.jgit.lib.CommitConfig.CleanupMode; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; @@ -83,9 +57,11 @@ import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.RebaseTodoLine; import org.eclipse.jgit.lib.RebaseTodoLine.Action; +import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.ReflogEntry; import org.eclipse.jgit.lib.RepositoryState; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevSort; @@ -103,6 +79,10 @@ public class RebaseCommandTest extends RepositoryTestCase { private static final String FILE1 = "file1"; + private static final String FILE2 = "file2"; + + private static final String FILE3 = "file3"; + protected Git git; @Override @@ -154,11 +134,12 @@ public class RebaseCommandTest extends RepositoryTestCase { checkFile(file2, "file2"); assertEquals(Status.FAST_FORWARD, res.getStatus()); - List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD) + RefDatabase refDb = db.getRefDatabase(); + List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD) .getReverseEntries(); - List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic") + List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic") .getReverseEntries(); - List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master") + List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master") .getReverseEntries(); assertEquals("rebase finished: returning to refs/heads/topic", headLog .get(0).getComment()); @@ -200,11 +181,12 @@ public class RebaseCommandTest extends RepositoryTestCase { checkFile(file2, "file2 new content"); assertEquals(Status.FAST_FORWARD, res.getStatus()); - List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD) + RefDatabase refDb = db.getRefDatabase(); + List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD) .getReverseEntries(); - List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic") + List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic") .getReverseEntries(); - List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master") + List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master") .getReverseEntries(); assertEquals("rebase finished: returning to refs/heads/topic", headLog .get(0).getComment()); @@ -218,6 +200,177 @@ public class RebaseCommandTest extends RepositoryTestCase { } /** + * Rebase a single root commit onto an independent branch. + * + * <pre> + * A (master) + * + * B - C (orphan) + * </pre> + * + * to + * + * <pre> + * A + * + * B - C (orphan) - A' (master) + * </pre> + * + * @throws Exception + */ + @Test + public void testRebaseRootCommit() throws Exception { + writeTrashFile(FILE1, FILE1); + writeTrashFile(FILE2, FILE2); + git.add().addFilepattern(FILE1).addFilepattern(FILE2).call(); + RevCommit first = git.commit().setMessage("Add files").call(); + File file1 = new File(db.getWorkTree(), FILE1); + File file2 = new File(db.getWorkTree(), FILE2); + assertTrue(file1.exists()); + assertTrue(file2.exists()); + // Create an independent branch + git.checkout().setOrphan(true).setName("orphan").call(); + git.rm().addFilepattern(FILE1).addFilepattern(FILE2).call(); + assertFalse(file1.exists()); + assertFalse(file2.exists()); + writeTrashFile(FILE1, "something else"); + git.add().addFilepattern(FILE1).call(); + RevCommit orphanBase = git.commit().setMessage("Orphan base").call(); + writeTrashFile(FILE1, FILE1); + git.add().addFilepattern(FILE1).call(); + RevCommit orphanTop = git.commit().setMessage("Same file1").call(); + checkoutBranch("refs/heads/master"); + assertEquals(first.getId(), db.resolve("HEAD")); + RebaseResult res = git.rebase().setUpstream("refs/heads/orphan").call(); + assertEquals(Status.OK, res.getStatus()); + Iterable<RevCommit> log = git.log().add(db.resolve("HEAD")).call(); + ObjectId[] ids = { orphanTop.getId(), orphanBase.getId() }; + int nOfCommits = 0; + for (RevCommit c : log) { + nOfCommits++; + if (nOfCommits == 1) { + assertEquals("Add files", c.getFullMessage()); + } else { + assertEquals(ids[nOfCommits - 2], c.getId()); + } + } + assertEquals(3, nOfCommits); + assertTrue(file1.exists()); + checkFile(file1, FILE1); + assertTrue(file2.exists()); + checkFile(file2, FILE2); + } + + /** + * Rebase a branch onto an independent branch. + * + * <pre> + * A - B (master) + * + * C - D (orphan) + * </pre> + * + * to + * + * <pre> + * A - B + * + * C - D (orphan) - A' - B' (master) + * </pre> + * + * @throws Exception + */ + @Test + public void testRebaseNoMergeBase() throws Exception { + writeTrashFile(FILE1, FILE1); + writeTrashFile(FILE2, FILE2); + git.add().addFilepattern(FILE1).addFilepattern(FILE2).call(); + git.commit().setMessage("Add files").call(); + writeTrashFile(FILE3, FILE3); + git.add().addFilepattern(FILE3).call(); + RevCommit first = git.commit().setMessage("File3").call(); + File file1 = new File(db.getWorkTree(), FILE1); + File file2 = new File(db.getWorkTree(), FILE2); + File file3 = new File(db.getWorkTree(), FILE3); + assertTrue(file1.exists()); + assertTrue(file2.exists()); + assertTrue(file3.exists()); + // Create an independent branch + git.checkout().setOrphan(true).setName("orphan").call(); + git.rm() + .addFilepattern(FILE1) + .addFilepattern(FILE2) + .addFilepattern(FILE3) + .call(); + assertFalse(file1.exists()); + assertFalse(file2.exists()); + assertFalse(file3.exists()); + writeTrashFile(FILE1, "something else"); + git.add().addFilepattern(FILE1).call(); + RevCommit orphanBase = git.commit().setMessage("Orphan base").call(); + writeTrashFile(FILE1, FILE1); + git.add().addFilepattern(FILE1).call(); + RevCommit orphanTop = git.commit().setMessage("Same file1").call(); + checkoutBranch("refs/heads/master"); + assertEquals(first.getId(), db.resolve("HEAD")); + RebaseResult res = git.rebase().setUpstream("refs/heads/orphan").call(); + assertEquals(Status.OK, res.getStatus()); + Iterable<RevCommit> log = git.log().add(db.resolve("HEAD")).call(); + String[] msgs = { "File3", "Add files" }; + ObjectId[] ids = { orphanTop.getId(), orphanBase.getId() }; + int nOfCommits = 0; + for (RevCommit c : log) { + nOfCommits++; + if (nOfCommits <= msgs.length) { + assertEquals(msgs[nOfCommits - 1], c.getFullMessage()); + } else { + assertEquals(ids[nOfCommits - msgs.length - 1], c.getId()); + } + } + assertEquals(4, nOfCommits); + assertTrue(file1.exists()); + checkFile(file1, FILE1); + assertTrue(file2.exists()); + checkFile(file2, FILE2); + assertTrue(file3.exists()); + checkFile(file3, FILE3); + } + + /** + * Create a commit A and an unrelated commit B creating the same file with + * different content. Then rebase A onto B. The rebase should stop with a + * conflict. + * + * @throws Exception + * on errors + */ + @Test + public void testRebaseNoMergeBaseConflict() throws Exception { + writeTrashFile(FILE1, FILE1); + git.add().addFilepattern(FILE1).call(); + RevCommit first = git.commit().setMessage("Add file").call(); + File file1 = new File(db.getWorkTree(), FILE1); + assertTrue(file1.exists()); + // Create an independent branch + git.checkout().setOrphan(true).setName("orphan").call(); + git.rm().addFilepattern(FILE1).call(); + assertFalse(file1.exists()); + writeTrashFile(FILE1, "something else"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("Orphan").call(); + checkoutBranch("refs/heads/master"); + assertEquals(first.getId(), db.resolve("HEAD")); + RebaseResult res = git.rebase().setUpstream("refs/heads/orphan").call(); + assertEquals(Status.STOPPED, res.getStatus()); + assertEquals(first, res.getCurrentCommit()); + checkFile(file1, "<<<<<<< Upstream, based on orphan\n" + + "something else\n" + + "=======\n" + + "file1\n" + + ">>>>>>> " + first.abbreviate(7).name() + " Add file\n"); + } + + /** * Create the following commits and then attempt to rebase topic onto * master. This will serialize the branches. * @@ -288,21 +441,23 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.OK, res.getStatus()); - RevWalk rw = new RevWalk(db); - rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic"))); - assertDerivedFrom(rw.next(), e); - assertDerivedFrom(rw.next(), d); - assertDerivedFrom(rw.next(), c); - assertEquals(b, rw.next()); - assertEquals(a, rw.next()); + try (RevWalk rw = new RevWalk(db)) { + rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic"))); + assertDerivedFrom(rw.next(), e); + assertDerivedFrom(rw.next(), d); + assertDerivedFrom(rw.next(), c); + assertEquals(b, rw.next()); + assertEquals(a, rw.next()); + } - List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD) + RefDatabase refDb = db.getRefDatabase(); + List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD) .getReverseEntries(); - List<ReflogEntry> sideLog = db.getReflogReader("refs/heads/side") + List<ReflogEntry> sideLog = refDb.getReflogReader("refs/heads/side") .getReverseEntries(); - List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic") + List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic") .getReverseEntries(); - List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master") + List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master") .getReverseEntries(); assertEquals("rebase finished: returning to refs/heads/topic", headLog .get(0).getComment()); @@ -354,8 +509,6 @@ public class RebaseCommandTest extends RepositoryTestCase { */ private void doTestRebasePreservingMerges(boolean testConflict) throws Exception { - RevWalk rw = new RevWalk(db); - // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); @@ -409,7 +562,9 @@ public class RebaseCommandTest extends RepositoryTestCase { f = git.commit().setMessage("commit f").call(); } else { assertEquals(MergeStatus.MERGED, result.getMergeStatus()); - f = rw.parseCommit(result.getNewHead()); + try (RevWalk rw = new RevWalk(db)) { + f = rw.parseCommit(result.getNewHead()); + } } RebaseResult res = git.rebase().setUpstream("refs/heads/master") @@ -453,23 +608,25 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals("file2", read("file2")); assertEquals("more change", read("file3")); - rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic"))); - RevCommit newF = rw.next(); - assertDerivedFrom(newF, f); - assertEquals(2, newF.getParentCount()); - RevCommit newD = rw.next(); - assertDerivedFrom(newD, d); - if (testConflict) - assertEquals("d new", readFile("conflict", newD)); - RevCommit newE = rw.next(); - assertDerivedFrom(newE, e); - if (testConflict) - assertEquals("e new", readFile("conflict", newE)); - assertEquals(newD, newF.getParent(0)); - assertEquals(newE, newF.getParent(1)); - assertDerivedFrom(rw.next(), c); - assertEquals(b, rw.next()); - assertEquals(a, rw.next()); + try (RevWalk rw = new RevWalk(db)) { + rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic"))); + RevCommit newF = rw.next(); + assertDerivedFrom(newF, f); + assertEquals(2, newF.getParentCount()); + RevCommit newD = rw.next(); + assertDerivedFrom(newD, d); + if (testConflict) + assertEquals("d new", readFile("conflict", newD)); + RevCommit newE = rw.next(); + assertDerivedFrom(newE, e); + if (testConflict) + assertEquals("e new", readFile("conflict", newE)); + assertEquals(newD, newF.getParent(0)); + assertEquals(newE, newF.getParent(1)); + assertDerivedFrom(rw.next(), c); + assertEquals(b, rw.next()); + assertEquals(a, rw.next()); + } } private String readFile(String path, RevCommit commit) throws IOException { @@ -517,88 +674,89 @@ public class RebaseCommandTest extends RepositoryTestCase { */ private void doTestRebasePreservingMergesWithUnrelatedSide( boolean testConflict) throws Exception { - RevWalk rw = new RevWalk(db); - rw.sort(RevSort.TOPO); - - writeTrashFile(FILE1, FILE1); - git.add().addFilepattern(FILE1).call(); - RevCommit a = git.commit().setMessage("commit a").call(); - - writeTrashFile("file2", "blah"); - git.add().addFilepattern("file2").call(); - RevCommit b = git.commit().setMessage("commit b").call(); - - // create a topic branch - createBranch(b, "refs/heads/topic"); - checkoutBranch("refs/heads/topic"); + try (RevWalk rw = new RevWalk(db)) { + rw.sort(RevSort.TOPO); - writeTrashFile("file3", "more changess"); - writeTrashFile(FILE1, "preparing conflict"); - git.add().addFilepattern("file3").addFilepattern(FILE1).call(); - RevCommit c = git.commit().setMessage("commit c").call(); + writeTrashFile(FILE1, FILE1); + git.add().addFilepattern(FILE1).call(); + RevCommit a = git.commit().setMessage("commit a").call(); - createBranch(a, "refs/heads/side"); - checkoutBranch("refs/heads/side"); - writeTrashFile("conflict", "e"); - writeTrashFile(FILE1, FILE1 + "\n" + "line 2"); - git.add().addFilepattern(".").call(); - RevCommit e = git.commit().setMessage("commit e").call(); + writeTrashFile("file2", "blah"); + git.add().addFilepattern("file2").call(); + RevCommit b = git.commit().setMessage("commit b").call(); - // switch back to topic and merge in side, creating d - checkoutBranch("refs/heads/topic"); - MergeResult result = git.merge().include(e) - .setStrategy(MergeStrategy.RESOLVE).call(); + // create a topic branch + createBranch(b, "refs/heads/topic"); + checkoutBranch("refs/heads/topic"); - assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); - assertEquals(result.getConflicts().keySet(), - Collections.singleton(FILE1)); - writeTrashFile(FILE1, "merge resolution"); - git.add().addFilepattern(FILE1).call(); - RevCommit d = git.commit().setMessage("commit d").call(); + writeTrashFile("file3", "more changess"); + writeTrashFile(FILE1, "preparing conflict"); + git.add().addFilepattern("file3").addFilepattern(FILE1).call(); + RevCommit c = git.commit().setMessage("commit c").call(); - RevCommit f = commitFile("file2", "new content two", "topic"); + createBranch(a, "refs/heads/side"); + checkoutBranch("refs/heads/side"); + writeTrashFile("conflict", "e"); + writeTrashFile(FILE1, FILE1 + "\n" + "line 2"); + git.add().addFilepattern(".").call(); + RevCommit e = git.commit().setMessage("commit e").call(); - checkoutBranch("refs/heads/master"); - writeTrashFile("fileg", "fileg"); - if (testConflict) - writeTrashFile("conflict", "g"); - git.add().addFilepattern(".").call(); - RevCommit g = git.commit().setMessage("commit g").call(); + // switch back to topic and merge in side, creating d + checkoutBranch("refs/heads/topic"); + MergeResult result = git.merge().include(e) + .setStrategy(MergeStrategy.RESOLVE).call(); - checkoutBranch("refs/heads/topic"); - RebaseResult res = git.rebase().setUpstream("refs/heads/master") - .setPreserveMerges(true).call(); - if (testConflict) { - assertEquals(Status.STOPPED, res.getStatus()); - assertEquals(Collections.singleton("conflict"), git.status().call() - .getConflicting()); - // resolve - writeTrashFile("conflict", "e"); - git.add().addFilepattern("conflict").call(); - res = git.rebase().setOperation(Operation.CONTINUE).call(); + assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); + assertEquals(result.getConflicts().keySet(), + Collections.singleton(FILE1)); + writeTrashFile(FILE1, "merge resolution"); + git.add().addFilepattern(FILE1).call(); + RevCommit d = git.commit().setMessage("commit d").call(); + + RevCommit f = commitFile("file2", "new content two", "topic"); + + checkoutBranch("refs/heads/master"); + writeTrashFile("fileg", "fileg"); + if (testConflict) + writeTrashFile("conflict", "g"); + git.add().addFilepattern(".").call(); + RevCommit g = git.commit().setMessage("commit g").call(); + + checkoutBranch("refs/heads/topic"); + RebaseResult res = git.rebase().setUpstream("refs/heads/master") + .setPreserveMerges(true).call(); + if (testConflict) { + assertEquals(Status.STOPPED, res.getStatus()); + assertEquals(Collections.singleton("conflict"), git.status().call() + .getConflicting()); + // resolve + writeTrashFile("conflict", "e"); + git.add().addFilepattern("conflict").call(); + res = git.rebase().setOperation(Operation.CONTINUE).call(); + } + assertEquals(Status.OK, res.getStatus()); + + assertEquals("merge resolution", read(FILE1)); + assertEquals("new content two", read("file2")); + assertEquals("more changess", read("file3")); + assertEquals("fileg", read("fileg")); + + rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic"))); + RevCommit newF = rw.next(); + assertDerivedFrom(newF, f); + RevCommit newD = rw.next(); + assertDerivedFrom(newD, d); + assertEquals(2, newD.getParentCount()); + RevCommit newC = rw.next(); + assertDerivedFrom(newC, c); + RevCommit newE = rw.next(); + assertEquals(e, newE); + assertEquals(newC, newD.getParent(0)); + assertEquals(e, newD.getParent(1)); + assertEquals(g, rw.next()); + assertEquals(b, rw.next()); + assertEquals(a, rw.next()); } - assertEquals(Status.OK, res.getStatus()); - - assertEquals("merge resolution", read(FILE1)); - assertEquals("new content two", read("file2")); - assertEquals("more changess", read("file3")); - assertEquals("fileg", read("fileg")); - - rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic"))); - RevCommit newF = rw.next(); - assertDerivedFrom(newF, f); - RevCommit newD = rw.next(); - assertDerivedFrom(newD, d); - assertEquals(2, newD.getParentCount()); - RevCommit newC = rw.next(); - assertDerivedFrom(newC, c); - RevCommit newE = rw.next(); - assertEquals(e, newE); - assertEquals(newC, newD.getParent(0)); - assertEquals(e, newD.getParent(1)); - assertEquals(g, rw.next()); - assertEquals(b, rw.next()); - assertEquals(a, rw.next()); } @Test @@ -614,9 +772,10 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult result = git.rebase().setUpstream(parent).call(); assertEquals(Status.UP_TO_DATE, result.getStatus()); - assertEquals(2, db.getReflogReader(Constants.HEAD).getReverseEntries() - .size()); - assertEquals(2, db.getReflogReader("refs/heads/master") + RefDatabase refDb = db.getRefDatabase(); + assertEquals(2, refDb.getReflogReader(Constants.HEAD) + .getReverseEntries().size()); + assertEquals(2, refDb.getReflogReader("refs/heads/master") .getReverseEntries().size()); } @@ -632,9 +791,10 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res = git.rebase().setUpstream(first).call(); assertEquals(Status.UP_TO_DATE, res.getStatus()); - assertEquals(1, db.getReflogReader(Constants.HEAD).getReverseEntries() - .size()); - assertEquals(1, db.getReflogReader("refs/heads/master") + RefDatabase refDb = db.getRefDatabase(); + assertEquals(1, refDb.getReflogReader(Constants.HEAD) + .getReverseEntries().size()); + assertEquals(1, refDb.getReflogReader("refs/heads/master") .getReverseEntries().size()); } @@ -687,14 +847,17 @@ public class RebaseCommandTest extends RepositoryTestCase { checkFile(theFile, "1master\n2\n3\ntopic\n"); // our old branch should be checked out again assertEquals("refs/heads/topic", db.getFullBranch()); - assertEquals(lastMasterChange, new RevWalk(db).parseCommit( - db.resolve(Constants.HEAD)).getParent(0)); + try (RevWalk rw = new RevWalk(db)) { + assertEquals(lastMasterChange, rw.parseCommit( + db.resolve(Constants.HEAD)).getParent(0)); + } assertEquals(origHead, db.readOrigHead()); - List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD) + RefDatabase refDb = db.getRefDatabase(); + List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD) .getReverseEntries(); - List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic") + List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic") .getReverseEntries(); - List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master") + List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master") .getReverseEntries(); assertEquals(2, masterLog.size()); assertEquals(3, topicLog.size()); @@ -737,11 +900,13 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.OK, res.getStatus()); checkFile(theFile, "1master\n2\n3\ntopic\n"); - assertEquals(lastMasterChange, new RevWalk(db).parseCommit( - db.resolve(Constants.HEAD)).getParent(0)); + try (RevWalk rw = new RevWalk(db)) { + assertEquals(lastMasterChange, rw.parseCommit( + db.resolve(Constants.HEAD)).getParent(0)); + } - List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD) - .getReverseEntries(); + List<ReflogEntry> headLog = db.getRefDatabase() + .getReflogReader(Constants.HEAD).getReverseEntries(); assertEquals(8, headLog.size()); assertEquals("rebase: change file1 in topic", headLog.get(0) .getComment()); @@ -785,8 +950,10 @@ public class RebaseCommandTest extends RepositoryTestCase { // our old branch should be checked out again assertEquals("refs/heads/file3", db.getFullBranch()); - assertEquals(addFile2, new RevWalk(db).parseCommit( - db.resolve(Constants.HEAD)).getParent(0)); + try (RevWalk rw = new RevWalk(db)) { + assertEquals(addFile2, rw.parseCommit( + db.resolve(Constants.HEAD)).getParent(0)); + } checkoutBranch("refs/heads/file2"); assertTrue(new File(db.getWorkTree(), FILE1).exists()); @@ -846,9 +1013,10 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(res.getStatus(), Status.ABORTED); assertEquals("refs/heads/topic", db.getFullBranch()); checkFile(FILE1, "1topic", "2", "3", "topic4"); - RevWalk rw = new RevWalk(db); - assertEquals(lastTopicCommit, rw - .parseCommit(db.resolve(Constants.HEAD))); + try (RevWalk rw = new RevWalk(db)) { + assertEquals(lastTopicCommit, + rw.parseCommit(db.resolve(Constants.HEAD))); + } assertEquals(RepositoryState.SAFE, db.getRepositoryState()); // rebase- dir in .git must be deleted @@ -909,9 +1077,10 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(res.getStatus(), Status.ABORTED); assertEquals(lastTopicCommit.getName(), db.getFullBranch()); checkFile(FILE1, "1topic", "2", "3", "topic4"); - RevWalk rw = new RevWalk(db); - assertEquals(lastTopicCommit, - rw.parseCommit(db.resolve(Constants.HEAD))); + try (RevWalk rw = new RevWalk(db)) { + assertEquals(lastTopicCommit, + rw.parseCommit(db.resolve(Constants.HEAD))); + } assertEquals(RepositoryState.SAFE, db.getRepositoryState()); // rebase- dir in .git must be deleted @@ -966,11 +1135,12 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(RepositoryState.SAFE, db.getRepositoryState()); ObjectId headId = db.resolve(Constants.HEAD); - RevWalk rw = new RevWalk(db); - RevCommit rc = rw.parseCommit(headId); - RevCommit parent = rw.parseCommit(rc.getParent(0)); - assertEquals("change file1 in topic\n\nThis is conflicting", parent - .getFullMessage()); + try (RevWalk rw = new RevWalk(db)) { + RevCommit rc = rw.parseCommit(headId); + RevCommit parent = rw.parseCommit(rc.getParent(0)); + assertEquals("change file1 in topic\n\nThis is conflicting", parent + .getFullMessage()); + } } @Test @@ -1017,9 +1187,10 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setOperation(Operation.SKIP).call(); ObjectId headId = db.resolve(Constants.HEAD); - RevWalk rw = new RevWalk(db); - RevCommit rc = rw.parseCommit(headId); - assertEquals("change file1 in master", rc.getFullMessage()); + try (RevWalk rw = new RevWalk(db)) { + RevCommit rc = rw.parseCommit(headId); + assertEquals("change file1 in master", rc.getFullMessage()); + } } @Test @@ -1308,10 +1479,11 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setOperation(Operation.SKIP).call(); ObjectId headId = db.resolve(Constants.HEAD); - RevWalk rw = new RevWalk(db); - RevCommit rc = rw.parseCommit(headId); - RevCommit parent = rw.parseCommit(rc.getParent(0)); - assertEquals("A different commit message", parent.getFullMessage()); + try (RevWalk rw = new RevWalk(db)) { + RevCommit rc = rw.parseCommit(headId); + RevCommit parent = rw.parseCommit(rc.getParent(0)); + assertEquals("A different commit message", parent.getFullMessage()); + } } private RevCommit writeFileAndCommit(String fileName, String commitMessage, @@ -1420,9 +1592,10 @@ public class RebaseCommandTest extends RepositoryTestCase { res = git.rebase().setOperation(Operation.ABORT).call(); assertEquals(res.getStatus(), Status.ABORTED); assertEquals("refs/heads/topic", db.getFullBranch()); - RevWalk rw = new RevWalk(db); - assertEquals(conflicting, rw.parseCommit(db.resolve(Constants.HEAD))); - assertEquals(RepositoryState.SAFE, db.getRepositoryState()); + try (RevWalk rw = new RevWalk(db)) { + assertEquals(conflicting, rw.parseCommit(db.resolve(Constants.HEAD))); + assertEquals(RepositoryState.SAFE, db.getRepositoryState()); + } // rebase- dir in .git must be deleted assertFalse(new File(db.getDirectory(), "rebase-merge").exists()); @@ -1439,7 +1612,7 @@ public class RebaseCommandTest extends RepositoryTestCase { public void testAuthorScriptConverter() throws Exception { // -1 h timezone offset PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com", - 123456789123L, -60); + Instant.ofEpochMilli(123456789123L), ZoneOffset.ofHours(-1)); String convertedAuthor = git.rebase().toAuthorScript(ident); String[] lines = convertedAuthor.split("\n"); assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]); @@ -1447,16 +1620,18 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals("GIT_AUTHOR_DATE='@123456789 -0100'", lines[2]); PersonIdent parsedIdent = git.rebase().parseAuthor( - convertedAuthor.getBytes("UTF-8")); + convertedAuthor.getBytes(UTF_8)); assertEquals(ident.getName(), parsedIdent.getName()); assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress()); // this is rounded to the last second - assertEquals(123456789000L, parsedIdent.getWhen().getTime()); - assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset()); + assertEquals(123456789000L, + parsedIdent.getWhenAsInstant().toEpochMilli()); + assertEquals(ident.getZoneId(), parsedIdent.getZoneId()); // + 9.5h timezone offset ident = new PersonIdent("Author name", "a.mail@some.com", - 123456789123L, +570); + Instant.ofEpochMilli(123456789123L), + ZoneOffset.ofHoursMinutes(9, 30)); convertedAuthor = git.rebase().toAuthorScript(ident); lines = convertedAuthor.split("\n"); assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]); @@ -1464,11 +1639,12 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals("GIT_AUTHOR_DATE='@123456789 +0930'", lines[2]); parsedIdent = git.rebase().parseAuthor( - convertedAuthor.getBytes("UTF-8")); + convertedAuthor.getBytes(UTF_8)); assertEquals(ident.getName(), parsedIdent.getName()); assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress()); - assertEquals(123456789000L, parsedIdent.getWhen().getTime()); - assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset()); + assertEquals(123456789000L, + parsedIdent.getWhenAsInstant().toEpochMilli()); + assertEquals(ident.getZoneId(), parsedIdent.getZoneId()); } @Test @@ -1964,6 +2140,60 @@ public class RebaseCommandTest extends RepositoryTestCase { } @Test + public void testRebaseWithAutoStashAndSubdirs() throws Exception { + // create file0, add and commit + db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null, + ConfigConstants.CONFIG_KEY_AUTOSTASH, true); + writeTrashFile("sub/file0", "file0"); + git.add().addFilepattern("sub/file0").call(); + git.commit().setMessage("commit0").call(); + // create file1, add and commit + writeTrashFile(FILE1, "file1"); + git.add().addFilepattern(FILE1).call(); + RevCommit commit = git.commit().setMessage("commit1").call(); + + // create topic branch and checkout / create file2, add and commit + createBranch(commit, "refs/heads/topic"); + checkoutBranch("refs/heads/topic"); + writeTrashFile("file2", "file2"); + git.add().addFilepattern("file2").call(); + git.commit().setMessage("commit2").call(); + + // checkout master branch / modify file1, add and commit + checkoutBranch("refs/heads/master"); + writeTrashFile(FILE1, "modified file1"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("commit3").call(); + + // checkout topic branch / modify file0 + checkoutBranch("refs/heads/topic"); + writeTrashFile("sub/file0", "unstaged modified file0"); + + ChangeRecorder recorder = new ChangeRecorder(); + ListenerHandle handle = db.getListenerList() + .addWorkingTreeModifiedListener(recorder); + try { + // rebase + assertEquals(Status.OK, git.rebase() + .setUpstream("refs/heads/master").call().getStatus()); + } finally { + handle.remove(); + } + checkFile(new File(new File(db.getWorkTree(), "sub"), "file0"), + "unstaged modified file0"); + checkFile(new File(db.getWorkTree(), FILE1), "modified file1"); + checkFile(new File(db.getWorkTree(), "file2"), "file2"); + assertEquals( + "[file1, mode:100644, content:modified file1]" + + "[file2, mode:100644, content:file2]" + + "[sub/file0, mode:100644, content:file0]", + indexState(CONTENT)); + assertEquals(RepositoryState.SAFE, db.getRepositoryState()); + recorder.assertEvent(new String[] { "file1", "file2", "sub/file0" }, + new String[0]); + } + + @Test public void testRebaseWithAutoStashConflictOnApply() throws Exception { // create file0, add and commit db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null, @@ -2037,7 +2267,7 @@ public class RebaseCommandTest extends RepositoryTestCase { checkoutBranch("refs/heads/master"); writeTrashFile(FILE1, "modified file1"); git.add().addFilepattern(FILE1).call(); - git.commit().setMessage("commit3").call(); + git.commit().setMessage("commit2").call(); // checkout topic branch / modify file0 checkoutBranch("refs/heads/topic"); @@ -2056,13 +2286,66 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(RepositoryState.SAFE, db.getRepositoryState()); } + @Test + public void testFastForwardRebaseWithAutoStashConflict() throws Exception { + // create file0, add and commit + db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null, + ConfigConstants.CONFIG_KEY_AUTOSTASH, true); + writeTrashFile("file0", "file0"); + git.add().addFilepattern("file0").call(); + git.commit().setMessage("commit0").call(); + // create file1, add and commit + writeTrashFile(FILE1, "file1"); + git.add().addFilepattern(FILE1).call(); + RevCommit commit = git.commit().setMessage("commit1").call(); + + // create topic branch + createBranch(commit, "refs/heads/topic"); + + // checkout master branch / modify file1, add and commit + checkoutBranch("refs/heads/master"); + writeTrashFile(FILE1, "modified file1"); + git.add().addFilepattern(FILE1).call(); + RevCommit master = git.commit().setMessage("commit2").call(); + + // checkout topic branch / modify file0 and file1 + checkoutBranch("refs/heads/topic"); + writeTrashFile("file0", "unstaged modified file0"); + writeTrashFile(FILE1, "unstaged modified file1"); + + // rebase + assertEquals(Status.STASH_APPLY_CONFLICTS, + git.rebase().setUpstream("refs/heads/master").call() + .getStatus()); + checkFile(new File(db.getWorkTree(), "file0"), + "unstaged modified file0"); + checkFile(new File(db.getWorkTree(), FILE1), + "<<<<<<< HEAD\n" + + "modified file1\n" + + "=======\n" + + "unstaged modified file1\n" + + ">>>>>>> stash\n"); + // If there is a merge conflict, the index is not reset, and thus file0 + // is staged here. This is the same behavior as in C git. + String expected = "[file0, mode:100644, content:unstaged modified file0]" + + "[file1, mode:100644, stage:1, content:file1]" + + "[file1, mode:100644, stage:2, content:modified file1]" + + "[file1, mode:100644, stage:3, content:unstaged modified file1]"; + assertEquals(expected, indexState(CONTENT)); + assertEquals(RepositoryState.SAFE, db.getRepositoryState()); + assertEquals(master, db.resolve(Constants.HEAD)); + assertEquals(master, db.resolve("refs/heads/topic")); + } + private List<DiffEntry> getStashedDiff() throws AmbiguousObjectException, IncorrectObjectTypeException, IOException, MissingObjectException { ObjectId stashId = db.resolve("stash@{0}"); - RevWalk revWalk = new RevWalk(db); - RevCommit stashCommit = revWalk.parseCommit(stashId); - List<DiffEntry> diffs = diffWorkingAgainstHead(stashCommit, revWalk); - return diffs; + try (RevWalk revWalk = new RevWalk(db)) { + RevCommit stashCommit = revWalk.parseCommit(stashId); + List<DiffEntry> diffs = diffWorkingAgainstHead(stashCommit, + revWalk); + return diffs; + } } private TreeWalk createTreeWalk() { @@ -2086,9 +2369,8 @@ public class RebaseCommandTest extends RepositoryTestCase { private int countPicks() throws IOException { int count = 0; File todoFile = getTodoFile(); - BufferedReader br = new BufferedReader(new InputStreamReader( - new FileInputStream(todoFile), "UTF-8")); - try { + try (BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(todoFile), UTF_8))) { String line = br.readLine(); while (line != null) { int firstBlank = line.indexOf(' '); @@ -2106,8 +2388,6 @@ public class RebaseCommandTest extends RepositoryTestCase { line = br.readLine(); } return count; - } finally { - br.close(); } } @@ -2253,11 +2533,13 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res = git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { steps.add(0, new RebaseTodoLine( "# Comment that should not be processed")); } + @Override public String modifyCommitMessage(String commit) { fail("modifyCommitMessage() was not expected to be called"); return commit; @@ -2268,6 +2550,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res2 = git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { // delete RevCommit c4 @@ -2277,6 +2560,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { fail("modifyCommitMessage() was not expected to be called"); return commit; @@ -2286,14 +2570,15 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(RebaseResult.Status.OK, res2.getStatus()); ObjectId headId = db.resolve(Constants.HEAD); - RevWalk rw = new RevWalk(db); - RevCommit rc = rw.parseCommit(headId); + try (RevWalk rw = new RevWalk(db)) { + RevCommit rc = rw.parseCommit(headId); - ObjectId head1Id = db.resolve(Constants.HEAD + "~1"); - RevCommit rc1 = rw.parseCommit(head1Id); + ObjectId head1Id = db.resolve(Constants.HEAD + "~1"); + RevCommit rc1 = rw.parseCommit(head1Id); - assertEquals(rc.getFullMessage(), c4.getFullMessage()); - assertEquals(rc1.getFullMessage(), c2.getFullMessage()); + assertEquals(rc.getFullMessage(), c4.getFullMessage()); + assertEquals(rc1.getFullMessage(), c2.getFullMessage()); + } } @Test @@ -2377,7 +2662,9 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals("1111111", firstLine.getCommit().name()); assertEquals("pick", firstLine.getAction().toToken()); } catch (Exception e) { - fail("Valid parsable RebaseTodoLine that has been commented out should allow to change the action, but failed"); + throw new AssertionError( + "Valid parsable RebaseTodoLine that has been commented out should allow to change the action, but failed", + e); } assertEquals("2222222", steps.get(1).getCommit().name()); @@ -2497,6 +2784,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res = git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.REWORD); @@ -2505,6 +2793,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return "rewritten commit message"; } @@ -2543,6 +2832,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res = git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.EDIT); @@ -2551,6 +2841,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return ""; // not used } @@ -2607,6 +2898,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setUpstream("HEAD~3") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(1).setAction(Action.SQUASH); @@ -2615,6 +2907,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { final File messageSquashFile = new File(db .getDirectory(), "rebase-merge/message-squash"); @@ -2643,15 +2936,16 @@ public class RebaseCommandTest extends RepositoryTestCase { } }).call(); - RevWalk walk = new RevWalk(db); - ObjectId headId = db.resolve(Constants.HEAD); - RevCommit headCommit = walk.parseCommit(headId); - assertEquals(headCommit.getFullMessage(), - "update file2 on master\nnew line"); - - ObjectId head2Id = db.resolve(Constants.HEAD + "^1"); - RevCommit head1Commit = walk.parseCommit(head2Id); - assertEquals("changed", head1Commit.getFullMessage()); + try (RevWalk walk = new RevWalk(db)) { + ObjectId headId = db.resolve(Constants.HEAD); + RevCommit headCommit = walk.parseCommit(headId); + assertEquals(headCommit.getFullMessage(), + "update file2 on master\nnew line"); + + ObjectId head2Id = db.resolve(Constants.HEAD + "^1"); + RevCommit head1Commit = walk.parseCommit(head2Id); + assertEquals("changed", head1Commit.getFullMessage()); + } } @Test @@ -2686,6 +2980,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setUpstream("HEAD~4") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(1).setAction(Action.SQUASH); @@ -2695,6 +2990,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { final File messageSquashFile = new File(db.getDirectory(), "rebase-merge/message-squash"); @@ -2722,17 +3018,18 @@ public class RebaseCommandTest extends RepositoryTestCase { } }).call(); - RevWalk walk = new RevWalk(db); - ObjectId headId = db.resolve(Constants.HEAD); - RevCommit headCommit = walk.parseCommit(headId); - assertEquals(headCommit.getFullMessage(), - "update file2 on master\nnew line"); - - ObjectId head2Id = db.resolve(Constants.HEAD + "^1"); - RevCommit head1Commit = walk.parseCommit(head2Id); - assertEquals( - "Add file1\nnew line\nAdd file2\nnew line\nupdated file1 on master\nnew line", - head1Commit.getFullMessage()); + try (RevWalk walk = new RevWalk(db)) { + ObjectId headId = db.resolve(Constants.HEAD); + RevCommit headCommit = walk.parseCommit(headId); + assertEquals(headCommit.getFullMessage(), + "update file2 on master\nnew line"); + + ObjectId head2Id = db.resolve(Constants.HEAD + "^1"); + RevCommit head1Commit = walk.parseCommit(head2Id); + assertEquals( + "Add file1\nnew line\nAdd file2\nnew line\nupdated file1 on master\nnew line", + head1Commit.getFullMessage()); + } } @Test @@ -2767,6 +3064,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setUpstream("HEAD~4") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(1).setAction(Action.FIXUP); @@ -2776,6 +3074,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { final File messageSquashFile = new File(db .getDirectory(), "rebase-merge/message-squash"); @@ -2804,15 +3103,16 @@ public class RebaseCommandTest extends RepositoryTestCase { } }).call(); - RevWalk walk = new RevWalk(db); - ObjectId headId = db.resolve(Constants.HEAD); - RevCommit headCommit = walk.parseCommit(headId); - assertEquals(headCommit.getFullMessage(), - "update file2 on master\nnew line"); - - ObjectId head2Id = db.resolve(Constants.HEAD + "^1"); - RevCommit head1Commit = walk.parseCommit(head2Id); - assertEquals("changed", head1Commit.getFullMessage()); + try (RevWalk walk = new RevWalk(db)) { + ObjectId headId = db.resolve(Constants.HEAD); + RevCommit headCommit = walk.parseCommit(headId); + assertEquals(headCommit.getFullMessage(), + "update file2 on master\nnew line"); + + ObjectId head2Id = db.resolve(Constants.HEAD + "^1"); + RevCommit head1Commit = walk.parseCommit(head2Id); + assertEquals("changed", head1Commit.getFullMessage()); + } } @Test @@ -2841,6 +3141,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setUpstream("HEAD~3") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(1).setAction(Action.FIXUP); @@ -2849,26 +3150,28 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { fail("No callback to modify commit message expected for single fixup"); return commit; } }).call(); - RevWalk walk = new RevWalk(db); - ObjectId headId = db.resolve(Constants.HEAD); - RevCommit headCommit = walk.parseCommit(headId); - assertEquals("update file2 on master\nnew line", - headCommit.getFullMessage()); - - ObjectId head1Id = db.resolve(Constants.HEAD + "^1"); - RevCommit head1Commit = walk.parseCommit(head1Id); - assertEquals("Add file2\nnew line", - head1Commit.getFullMessage()); + try (RevWalk walk = new RevWalk(db)) { + ObjectId headId = db.resolve(Constants.HEAD); + RevCommit headCommit = walk.parseCommit(headId); + assertEquals("update file2 on master\nnew line", + headCommit.getFullMessage()); + + ObjectId head1Id = db.resolve(Constants.HEAD + "^1"); + RevCommit head1Commit = walk.parseCommit(head1Id); + assertEquals("Add file2\nnew line", + head1Commit.getFullMessage()); + } } - @Test - public void testRebaseInteractiveFixupWithBlankLines() throws Exception { + private void simpleFixup(String firstMessage, String secondMessage) + throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); @@ -2878,17 +3181,18 @@ public class RebaseCommandTest extends RepositoryTestCase { // create file2 on master writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); - git.commit().setMessage("Add file2").call(); + git.commit().setMessage(firstMessage).call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); // update FILE1 on master writeTrashFile(FILE1, "blah"); git.add().addFilepattern(FILE1).call(); - git.commit().setMessage("updated file1 on master\n\nsome text").call(); + git.commit().setMessage(secondMessage).call(); git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(1).setAction(Action.FIXUP); @@ -2897,17 +3201,41 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { fail("No callback to modify commit message expected for single fixup"); return commit; } }).call(); - RevWalk walk = new RevWalk(db); - ObjectId headId = db.resolve(Constants.HEAD); - RevCommit headCommit = walk.parseCommit(headId); - assertEquals("Add file2", - headCommit.getFullMessage()); + try (RevWalk walk = new RevWalk(db)) { + ObjectId headId = db.resolve(Constants.HEAD); + RevCommit headCommit = walk.parseCommit(headId); + assertEquals(firstMessage, headCommit.getFullMessage()); + } + + } + + @Test + public void testRebaseInteractiveFixupWithBlankLines() throws Exception { + simpleFixup("Add file2", "updated file1 on master\n\nsome text"); + } + + @Test + public void testRebaseInteractiveFixupWithBlankLines2() throws Exception { + simpleFixup("Add file2\n\nBody\n", + "updated file1 on master\n\nsome text"); + } + + @Test + public void testRebaseInteractiveFixupWithHash() throws Exception { + simpleFixup("#Add file2", "updated file1 on master"); + } + + @Test + public void testRebaseInteractiveFixupWithHash2() throws Exception { + simpleFixup("#Add file2\n\nHeader has hash\n", + "#updated file1 on master"); } @Test(expected = InvalidRebaseStepException.class) @@ -2928,6 +3256,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setUpstream("HEAD~1") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.FIXUP); @@ -2936,6 +3265,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return commit; } @@ -2960,6 +3290,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setUpstream("HEAD~1") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.SQUASH); @@ -2968,6 +3299,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return commit; } @@ -2991,6 +3323,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.rebase().setUpstream("HEAD~1") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.EDIT); @@ -2999,6 +3332,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return commit; } @@ -3033,6 +3367,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult result = git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { steps.remove(0); try { @@ -3042,6 +3377,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return commit; } @@ -3075,6 +3411,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult result = git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { steps.remove(0); try { @@ -3084,6 +3421,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return "rewritten commit message"; } @@ -3092,6 +3430,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.add().addFilepattern(FILE1).call(); result = git.rebase().runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { steps.remove(0); try { @@ -3101,6 +3440,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return "rewritten commit message"; } @@ -3138,6 +3478,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult result = git.rebase().setUpstream("HEAD~3") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.PICK); @@ -3148,6 +3489,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return "squashed message"; } @@ -3156,6 +3498,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.add().addFilepattern(FILE1).call(); result = git.rebase().runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.PICK); @@ -3166,6 +3509,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return "squashed message"; } @@ -3204,6 +3548,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult result = git.rebase().setUpstream("HEAD~3") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.PICK); @@ -3214,6 +3559,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return commit; } @@ -3222,6 +3568,7 @@ public class RebaseCommandTest extends RepositoryTestCase { git.add().addFilepattern(FILE1).call(); result = git.rebase().runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.PICK); @@ -3232,6 +3579,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return "commit"; } @@ -3275,6 +3623,7 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult result = git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { + @Override public void prepareSteps(List<RebaseTodoLine> steps) { try { steps.get(0).setAction(Action.EDIT); @@ -3284,6 +3633,7 @@ public class RebaseCommandTest extends RepositoryTestCase { } } + @Override public String modifyCommitMessage(String commit) { return commit; } @@ -3303,6 +3653,99 @@ public class RebaseCommandTest extends RepositoryTestCase { } + @Test + public void testInteractiveRebaseSquashFixupSequence() throws Exception { + // create file1, add and commit + writeTrashFile(FILE1, "file1"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("commit1").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("commit2").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1 a second time"); + git.add().addFilepattern(FILE1).call(); + // Make it difficult; use git standard comment characters in the commit + // messages + git.commit().setMessage("#commit3").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1 a third time"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("@commit4").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1 a fourth time"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage(";commit5").call(); + + StoredConfig config = git.getRepository().getConfig(); + config.setString("core", null, "commentChar", "auto"); + // With "auto", we should end up with '@' being used as comment + // character (commit4 is skipped, so it should not advance the + // character). + RebaseResult result = git.rebase().setUpstream("HEAD~4") + .runInteractively(new InteractiveHandler2() { + + @Override + public void prepareSteps(List<RebaseTodoLine> steps) { + try { + steps.get(0).setAction(Action.PICK); + steps.get(1).setAction(Action.SQUASH); + steps.get(2).setAction(Action.FIXUP); + steps.get(3).setAction(Action.SQUASH); + } catch (IllegalTodoFileModification e) { + fail("unexpected exception: " + e); + } + } + + @Override + public String modifyCommitMessage(String commit) { + fail("should not be called"); + return commit; + } + + @Override + public ModifyResult editCommitMessage(String message, + CleanupMode mode, char commentChar) { + assertEquals('@', commentChar); + assertEquals("@ This is a combination of 4 commits.\n" + + "@ The first commit's message is:\n" + + "commit2\n" + + "@ This is the 2nd commit message:\n" + + "#commit3\n" + + "@ The 3rd commit message will be skipped:\n" + + "@ @commit4\n" + + "@ This is the 4th commit message:\n" + + ";commit5", message); + return new ModifyResult() { + + @Override + public String getMessage() { + return message; + } + + @Override + public CleanupMode getCleanupMode() { + return mode; + } + + @Override + public boolean shouldAddChangeId() { + return false; + } + }; + } + }).call(); + assertEquals(Status.OK, result.getStatus()); + Iterator<RevCommit> logIterator = git.log().all().call().iterator(); + String actualCommitMsg = logIterator.next().getFullMessage(); + assertEquals("commit2\n#commit3\n;commit5", actualCommitMsg); + } + private File getTodoFile() { File todoFile = new File(db.getDirectory(), GIT_REBASE_TODO); return todoFile; |