/* * Copyright (C) 2010, Mathias Kinzler * 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.api; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import org.eclipse.jgit.api.MergeResult.MergeStatus; import org.eclipse.jgit.api.RebaseCommand.Action; import org.eclipse.jgit.api.RebaseCommand.Operation; import org.eclipse.jgit.api.RebaseResult.Status; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.UnmergedPathsException; import org.eclipse.jgit.api.errors.WrongRepositoryStateException; import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.RepositoryTestCase; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.junit.Before; import org.junit.Test; public class RebaseCommandTest extends RepositoryTestCase { private static final String FILE1 = "file1"; protected Git git; @Override @Before public void setUp() throws Exception { super.setUp(); this.git = new Git(db); } private void checkoutCommit(RevCommit commit) throws IllegalStateException, IOException { RevWalk walk = new RevWalk(db); RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD)); DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(), db .lockDirCache(), commit.getTree()); dco.setFailOnConflict(true); dco.checkout(); walk.release(); // update the HEAD RefUpdate refUpdate = db.updateRef(Constants.HEAD, true); refUpdate.setNewObjectId(commit); refUpdate.forceUpdate(); } @Test public void testFastForwardWithNewFile() throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); RevCommit first = git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // create a topic branch createBranch(first, "refs/heads/topic"); // create file2 on master File file2 = writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); git.commit().setMessage("Add file2").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); checkoutBranch("refs/heads/topic"); assertFalse(new File(db.getWorkTree(), "file2").exists()); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); checkFile(file2, "file2"); assertEquals(Status.FAST_FORWARD, res.getStatus()); } @Test public void testFastForwardWithMultipleCommits() throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); RevCommit first = git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // create a topic branch createBranch(first, "refs/heads/topic"); // create file2 on master File file2 = writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); git.commit().setMessage("Add file2").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); // write a second commit writeTrashFile("file2", "file2 new content"); git.add().addFilepattern("file2").call(); git.commit().setMessage("Change content of file2").call(); checkoutBranch("refs/heads/topic"); assertFalse(new File(db.getWorkTree(), "file2").exists()); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); checkFile(file2, "file2 new content"); assertEquals(Status.FAST_FORWARD, res.getStatus()); } @Test public void testRebaseFailsCantCherryPickMergeCommits() throws Exception { /** * Create the following commits and then attempt to rebase topic onto * master. This will fail as the cherry-pick list C, D, E an F contains * a merge commit (F). * *
		 * A - B (master)
		 *   \
		 *    C - D - F (topic)
		 *     \      /
		 *      E  -  (side)
		 * 
*/ // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); RevCommit first = git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // create a topic branch createBranch(first, "refs/heads/topic"); // update FILE1 on master writeTrashFile(FILE1, "blah"); git.add().addFilepattern(FILE1).call(); git.commit().setMessage("updated file1 on master").call(); checkoutBranch("refs/heads/topic"); writeTrashFile("file3", "more changess"); git.add().addFilepattern("file3").call(); RevCommit topicCommit = git.commit() .setMessage("update file3 on topic").call(); // create a branch from the topic commit createBranch(topicCommit, "refs/heads/side"); // second commit on topic writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); git.commit().setMessage("Add file2").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); // switch to side branch and update file2 checkoutBranch("refs/heads/side"); writeTrashFile("file3", "more change"); git.add().addFilepattern("file3").call(); RevCommit sideCommit = git.commit().setMessage("update file2 on side") .call(); // switch back to topic and merge in side checkoutBranch("refs/heads/topic"); MergeResult result = git.merge().include(sideCommit.getId()) .setStrategy(MergeStrategy.RESOLVE).call(); assertEquals(MergeStatus.MERGED, result.getMergeStatus()); try { RebaseResult rebase = git.rebase().setUpstream("refs/heads/master") .call(); fail("MultipleParentsNotAllowedException expected: " + rebase.getStatus()); } catch (JGitInternalException e) { // expected } } @Test public void testRebaseParentOntoHeadShouldBeUptoDate() throws Exception { writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); RevCommit parent = git.commit().setMessage("parent comment").call(); writeTrashFile(FILE1, "another change"); git.add().addFilepattern(FILE1).call(); git.commit().setMessage("head commit").call(); RebaseResult result = git.rebase().setUpstream(parent).call(); assertEquals(Status.UP_TO_DATE, result.getStatus()); } @Test public void testUpToDate() throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); RevCommit first = git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); RebaseResult res = git.rebase().setUpstream(first).call(); assertEquals(Status.UP_TO_DATE, res.getStatus()); } @Test public void testUnknownUpstream() throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); try { git.rebase().setUpstream("refs/heads/xyz").call(); fail("expected exception was not thrown"); } catch (RefNotFoundException e) { // expected exception } } @Test public void testConflictFreeWithSingleFile() throws Exception { // create file1 on master File theFile = writeTrashFile(FILE1, "1\n2\n3\n"); git.add().addFilepattern(FILE1).call(); RevCommit second = git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // change first line in master and commit writeTrashFile(FILE1, "1master\n2\n3\n"); checkFile(theFile, "1master\n2\n3\n"); git.add().addFilepattern(FILE1).call(); RevCommit lastMasterChange = git.commit().setMessage( "change file1 in master").call(); // create a topic branch based on second commit createBranch(second, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(theFile, "1\n2\n3\n"); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // change third line in topic branch writeTrashFile(FILE1, "1\n2\n3\ntopic\n"); git.add().addFilepattern(FILE1).call(); git.commit().setMessage("change file1 in topic").call(); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.OK, res.getStatus()); 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)); } @Test public void testDetachedHead() throws Exception { // create file1 on master File theFile = writeTrashFile(FILE1, "1\n2\n3\n"); git.add().addFilepattern(FILE1).call(); RevCommit second = git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // change first line in master and commit writeTrashFile(FILE1, "1master\n2\n3\n"); checkFile(theFile, "1master\n2\n3\n"); git.add().addFilepattern(FILE1).call(); RevCommit lastMasterChange = git.commit().setMessage( "change file1 in master").call(); // create a topic branch based on second commit createBranch(second, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(theFile, "1\n2\n3\n"); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // change third line in topic branch writeTrashFile(FILE1, "1\n2\n3\ntopic\n"); git.add().addFilepattern(FILE1).call(); RevCommit topicCommit = git.commit() .setMessage("change file1 in topic").call(); checkoutBranch("refs/heads/master"); checkoutCommit(topicCommit); assertEquals(topicCommit.getId().getName(), db.getFullBranch()); 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)); } @Test public void testFilesAddedFromTwoBranches() throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); RevCommit masterCommit = git.commit().setMessage("Add file1 to master") .call(); // create a branch named file2 and add file2 createBranch(masterCommit, "refs/heads/file2"); checkoutBranch("refs/heads/file2"); writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); RevCommit addFile2 = git.commit().setMessage( "Add file2 to branch file2").call(); // create a branch named file3 and add file3 createBranch(masterCommit, "refs/heads/file3"); checkoutBranch("refs/heads/file3"); writeTrashFile("file3", "file3"); git.add().addFilepattern("file3").call(); git.commit().setMessage("Add file3 to branch file3").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); assertFalse(new File(db.getWorkTree(), "file2").exists()); assertTrue(new File(db.getWorkTree(), "file3").exists()); RebaseResult res = git.rebase().setUpstream("refs/heads/file2").call(); assertEquals(Status.OK, res.getStatus()); assertTrue(new File(db.getWorkTree(), FILE1).exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); assertTrue(new File(db.getWorkTree(), "file3").exists()); // 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)); checkoutBranch("refs/heads/file2"); assertTrue(new File(db.getWorkTree(), FILE1).exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); assertFalse(new File(db.getWorkTree(), "file3").exists()); } @Test public void testStopOnConflict() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change first line in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on second commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "topic4"); // change first line (conflicting) RevCommit conflicting = writeFileAndCommit(FILE1, "change file1 in topic", "1topic", "2", "3", "topic4"); RevCommit lastTopicCommit = writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2", "3", "topic4"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); assertEquals(conflicting, res.getCurrentCommit()); checkFile(FILE1, "<<<<<<< OURS\n1master\n=======\n1topic\n>>>>>>> THEIRS\n2\n3\ntopic4"); assertEquals(RepositoryState.REBASING_INTERACTIVE, db .getRepositoryState()); assertTrue(new File(db.getDirectory(), "rebase-merge").exists()); // the first one should be included, so we should have left two picks in // the file assertEquals(1, countPicks()); // rebase should not succeed in this state try { git.rebase().setUpstream("refs/heads/master").call(); fail("Expected exception was not thrown"); } catch (WrongRepositoryStateException e) { // expected } // abort should reset to topic branch res = git.rebase().setOperation(Operation.ABORT).call(); 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))); assertEquals(RepositoryState.SAFE, db.getRepositoryState()); // rebase- dir in .git must be deleted assertFalse(new File(db.getDirectory(), "rebase-merge").exists()); } @Test public void testStopOnConflictAndContinue() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "4topic"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4topic"); // change second line (not conflicting) writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2topic", "3", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); // continue should throw a meaningful exception try { res = git.rebase().setOperation(Operation.CONTINUE).call(); fail("Expected Exception not thrown"); } catch (UnmergedPathsException e) { // expected } // merge the file; the second topic commit should go through writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic"); res = git.rebase().setOperation(Operation.CONTINUE).call(); assertNotNull(res); assertEquals(Status.OK, res.getStatus()); 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()); } @Test public void testStopOnConflictAndContinueWithNoDeltaToMaster() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); // continue should throw a meaningful exception try { res = git.rebase().setOperation(Operation.CONTINUE).call(); fail("Expected Exception not thrown"); } catch (UnmergedPathsException e) { // expected } // merge the file; the second topic commit should go through writeFileAndAdd(FILE1, "1master", "2", "3"); res = git.rebase().setOperation(Operation.CONTINUE).call(); assertNotNull(res); assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus()); assertEquals(RepositoryState.REBASING_INTERACTIVE, db.getRepositoryState()); 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()); } @Test public void testStopOnConflictAndFailContinueIfFileIsDirty() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "4topic"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4topic"); // change second line (not conflicting) writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2topic", "3", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); git.add().addFilepattern(FILE1).call(); File trashFile = writeTrashFile(FILE1, "Some local change"); res = git.rebase().setOperation(Operation.CONTINUE).call(); assertNotNull(res); assertEquals(Status.STOPPED, res.getStatus()); checkFile(trashFile, "Some local change"); } @Test public void testStopOnLastConflictAndContinue() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "4topic"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); // merge the file; the second topic commit should go through writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic"); res = git.rebase().setOperation(Operation.CONTINUE).call(); assertNotNull(res); assertEquals(Status.OK, res.getStatus()); assertEquals(RepositoryState.SAFE, db.getRepositoryState()); } @Test public void testStopOnLastConflictAndSkip() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "4topic"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); // merge the file; the second topic commit should go through writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic"); res = git.rebase().setOperation(Operation.SKIP).call(); assertNotNull(res); assertEquals(Status.OK, res.getStatus()); assertEquals(RepositoryState.SAFE, db.getRepositoryState()); } @Test public void testMergeFirstStopOnLastConflictAndSkip() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1topic", "2", "3", "4topic"); // change first line (conflicting again) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topicagain", "2", "3", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); writeFileAndAdd(FILE1, "merged"); res = git.rebase().setOperation(Operation.CONTINUE).call(); assertEquals(Status.STOPPED, res.getStatus()); res = git.rebase().setOperation(Operation.SKIP).call(); assertNotNull(res); assertEquals(Status.OK, res.getStatus()); assertEquals(RepositoryState.SAFE, db.getRepositoryState()); checkFile(FILE1, "merged"); } @Test public void testStopOnConflictAndSkipNoConflict() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "4topic"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4topic"); // change third line (not conflicting) writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2", "3topic", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); res = git.rebase().setOperation(Operation.SKIP).call(); checkFile(FILE1, "1master", "2", "3topic", "4topic"); assertEquals(Status.OK, res.getStatus()); } @Test public void testStopOnConflictAndSkipWithConflict() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3", "4"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3master", "4"); checkFile(FILE1, "1master", "2", "3master", "4"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3", "4"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "4", "5topic"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4", "5topic"); // change third line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2", "3topic", "4", "5topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); res = git.rebase().setOperation(Operation.SKIP).call(); // TODO is this correct? It is what the command line returns checkFile(FILE1, "1master\n2\n<<<<<<< OURS\n3master\n=======\n3topic\n>>>>>>> THEIRS\n4\n5topic"); assertEquals(Status.STOPPED, res.getStatus()); } @Test public void testStopOnConflictCommitAndContinue() throws Exception { // create file1 on master RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1", "2", "3"); // change in master writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3"); checkFile(FILE1, "1master", "2", "3"); // create a topic branch based on the first commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); // we have the old content again checkFile(FILE1, "1", "2", "3"); // add a line (non-conflicting) writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2", "3", "4topic"); // change first line (conflicting) writeFileAndCommit(FILE1, "change file1 in topic\n\nThis is conflicting", "1topic", "2", "3", "4topic"); // change second line (not conflicting) writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2", "3topic", "4topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); // continue should throw a meaningful exception try { res = git.rebase().setOperation(Operation.CONTINUE).call(); fail("Expected Exception not thrown"); } catch (UnmergedPathsException e) { // expected } // merge the file; the second topic commit should go through writeFileAndCommit(FILE1, "A different commit message", "1topic", "2", "3", "4topic"); res = git.rebase().setOperation(Operation.CONTINUE).call(); assertNotNull(res); // nothing to commit. this leaves the repo state in rebase, so that the // user can decide what to do. if he accidentally committed, reset soft, // and continue, if he really has nothing to commit, skip. assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus()); assertEquals(RepositoryState.REBASING_INTERACTIVE, db.getRepositoryState()); 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()); } private RevCommit writeFileAndCommit(String fileName, String commitMessage, String... lines) throws Exception { StringBuilder sb = new StringBuilder(); for (String line : lines) { sb.append(line); sb.append('\n'); } writeTrashFile(fileName, sb.toString()); git.add().addFilepattern(fileName).call(); return git.commit().setMessage(commitMessage).call(); } private void writeFileAndAdd(String fileName, String... lines) throws Exception { StringBuilder sb = new StringBuilder(); for (String line : lines) { sb.append(line); sb.append('\n'); } writeTrashFile(fileName, sb.toString()); git.add().addFilepattern(fileName).call(); } private void checkFile(String fileName, String... lines) throws Exception { File file = new File(db.getWorkTree(), fileName); StringBuilder sb = new StringBuilder(); for (String line : lines) { sb.append(line); sb.append('\n'); } checkFile(file, sb.toString()); } @Test public void testStopOnConflictFileCreationAndDeletion() throws Exception { // create file1 on master writeTrashFile(FILE1, "Hello World"); git.add().addFilepattern(FILE1).call(); // create file2 on master File file2 = writeTrashFile("file2", "Hello World 2"); git.add().addFilepattern("file2").call(); // create file3 on master File file3 = writeTrashFile("file3", "Hello World 3"); git.add().addFilepattern("file3").call(); RevCommit firstInMaster = git.commit() .setMessage("Add file 1, 2 and 3").call(); // create file4 on master File file4 = writeTrashFile("file4", "Hello World 4"); git.add().addFilepattern("file4").call(); deleteTrashFile("file2"); git.add().setUpdate(true).addFilepattern("file2").call(); // create folder folder6 on topic (conflicts with file folder6 on topic // later on) writeTrashFile("folder6/file1", "Hello World folder6"); git.add().addFilepattern("folder6/file1").call(); git.commit().setMessage( "Add file 4 and folder folder6, delete file2 on master").call(); // create a topic branch based on second commit createBranch(firstInMaster, "refs/heads/topic"); checkoutBranch("refs/heads/topic"); deleteTrashFile("file3"); git.add().setUpdate(true).addFilepattern("file3").call(); // create file5 on topic File file5 = writeTrashFile("file5", "Hello World 5"); git.add().addFilepattern("file5").call(); git.commit().setMessage("Delete file3 and add file5 in topic").call(); // create file folder6 on topic (conflicts with folder6 on master) writeTrashFile("folder6", "Hello World 6"); git.add().addFilepattern("folder6").call(); // create file7 on topic File file7 = writeTrashFile("file7", "Hello World 7"); git.add().addFilepattern("file7").call(); deleteTrashFile("file5"); git.add().setUpdate(true).addFilepattern("file5").call(); RevCommit conflicting = git.commit().setMessage( "Delete file5, add file folder6 and file7 in topic").call(); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertEquals(Status.STOPPED, res.getStatus()); assertEquals(conflicting, res.getCurrentCommit()); assertEquals(RepositoryState.REBASING_INTERACTIVE, db .getRepositoryState()); assertTrue(new File(db.getDirectory(), "rebase-merge").exists()); // the first one should be included, so we should have left two picks in // the file assertEquals(0, countPicks()); assertFalse(file2.exists()); assertFalse(file3.exists()); assertTrue(file4.exists()); assertFalse(file5.exists()); assertTrue(file7.exists()); // abort should reset to topic branch 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()); // rebase- dir in .git must be deleted assertFalse(new File(db.getDirectory(), "rebase-merge").exists()); assertTrue(file2.exists()); assertFalse(file3.exists()); assertFalse(file4.exists()); assertFalse(file5.exists()); assertTrue(file7.exists()); } @Test public void testAuthorScriptConverter() throws Exception { // -1 h timezone offset PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com", 123456789123L, -60); String convertedAuthor = git.rebase().toAuthorScript(ident); String[] lines = convertedAuthor.split("\n"); assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]); assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]); assertEquals("GIT_AUTHOR_DATE='123456789 -0100'", lines[2]); PersonIdent parsedIdent = git.rebase().parseAuthor( 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()); // + 9.5h timezone offset ident = new PersonIdent("Author name", "a.mail@some.com", 123456789123L, +570); convertedAuthor = git.rebase().toAuthorScript(ident); lines = convertedAuthor.split("\n"); assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]); assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]); assertEquals("GIT_AUTHOR_DATE='123456789 +0930'", lines[2]); parsedIdent = git.rebase().parseAuthor( convertedAuthor.getBytes("UTF-8")); assertEquals(ident.getName(), parsedIdent.getName()); assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress()); assertEquals(123456789000L, parsedIdent.getWhen().getTime()); assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset()); } @Test public void testRepositoryStateChecks() throws Exception { try { git.rebase().setOperation(Operation.ABORT).call(); fail("Expected Exception not thrown"); } catch (WrongRepositoryStateException e) { // expected } try { git.rebase().setOperation(Operation.SKIP).call(); fail("Expected Exception not thrown"); } catch (WrongRepositoryStateException e) { // expected } try { git.rebase().setOperation(Operation.CONTINUE).call(); fail("Expected Exception not thrown"); } catch (WrongRepositoryStateException e) { // expected } } @Test public void testRebaseWithUntrackedFile() throws Exception { // 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 / create untracked file3 checkoutBranch("refs/heads/topic"); writeTrashFile("file3", "untracked file3"); // rebase assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master") .call().getStatus()); } @Test @SuppressWarnings("null") public void testRebaseWithUnstagedTopicChange() throws Exception { // 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 file2 checkoutBranch("refs/heads/topic"); writeTrashFile("file2", "unstaged file2"); // rebase JGitInternalException exception = null; try { git.rebase().setUpstream("refs/heads/master").call(); } catch (JGitInternalException e) { exception = e; } assertNotNull(exception); assertEquals("Checkout conflict with files: \nfile2", exception.getMessage()); } @Test @SuppressWarnings("null") public void testRebaseWithUncommittedTopicChange() throws Exception { // 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 file2 and add checkoutBranch("refs/heads/topic"); File uncommittedFile = writeTrashFile("file2", "uncommitted file2"); git.add().addFilepattern("file2").call(); // do not commit // rebase JGitInternalException exception = null; try { git.rebase().setUpstream("refs/heads/master").call(); } catch (JGitInternalException e) { exception = e; } assertNotNull(exception); assertEquals("Checkout conflict with files: \nfile2", exception.getMessage()); checkFile(uncommittedFile, "uncommitted file2"); assertEquals(RepositoryState.SAFE, git.getRepository().getRepositoryState()); } @Test @SuppressWarnings("null") public void testRebaseWithUnstagedMasterChange() throws Exception { // 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 file1 checkoutBranch("refs/heads/topic"); writeTrashFile(FILE1, "unstaged modified file1"); // rebase JGitInternalException exception = null; try { git.rebase().setUpstream("refs/heads/master").call(); } catch (JGitInternalException e) { exception = e; } assertNotNull(exception); assertEquals("Checkout conflict with files: \nfile1", exception.getMessage()); } @Test @SuppressWarnings("null") public void testRebaseWithUncommittedMasterChange() throws Exception { // 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 file1 and add checkoutBranch("refs/heads/topic"); writeTrashFile(FILE1, "uncommitted modified file1"); git.add().addFilepattern(FILE1).call(); // do not commit // rebase JGitInternalException exception = null; try { git.rebase().setUpstream("refs/heads/master").call(); } catch (JGitInternalException e) { exception = e; } assertNotNull(exception); assertEquals("Checkout conflict with files: \nfile1", exception.getMessage()); } @Test public void testRebaseWithUnstagedMasterChangeBaseCommit() throws Exception { // create file0 + file1, add and commit writeTrashFile("file0", "file0"); writeTrashFile(FILE1, "file1"); git.add().addFilepattern("file0").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("file0", "unstaged modified file0"); // rebase assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master") .call().getStatus()); } @Test public void testRebaseWithUncommittedMasterChangeBaseCommit() throws Exception { // create file0 + file1, add and commit File file0 = writeTrashFile("file0", "file0"); writeTrashFile(FILE1, "file1"); git.add().addFilepattern("file0").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 and add checkoutBranch("refs/heads/topic"); write(file0, "unstaged modified file0"); git.add().addFilepattern("file0").call(); // do not commit // get current index state String indexState = indexState(CONTENT); // rebase RebaseResult result = git.rebase().setUpstream("refs/heads/master") .call(); assertEquals(Status.FAILED, result.getStatus()); // staged file0 causes DIRTY_INDEX assertEquals(1, result.getFailingPaths().size()); assertEquals(MergeFailureReason.DIRTY_INDEX, result.getFailingPaths() .get("file0")); assertEquals("unstaged modified file0", read(file0)); // index shall be unchanged assertEquals(indexState, indexState(CONTENT)); assertEquals(RepositoryState.SAFE, db.getRepositoryState()); } @Test public void testRebaseWithUnstagedMasterChangeOtherCommit() throws Exception { // create file0, add and commit 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 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("file0", "unstaged modified file0"); // rebase assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master") .call().getStatus()); } @Test public void testRebaseWithUncommittedMasterChangeOtherCommit() throws Exception { // create file0, add and commit File file0 = 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 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 and add checkoutBranch("refs/heads/topic"); write(file0, "unstaged modified file0"); git.add().addFilepattern("file0").call(); // do not commit // get current index state String indexState = indexState(CONTENT); // rebase RebaseResult result = git.rebase().setUpstream("refs/heads/master") .call(); assertEquals(Status.FAILED, result.getStatus()); // staged file0 causes DIRTY_INDEX assertEquals(1, result.getFailingPaths().size()); assertEquals(MergeFailureReason.DIRTY_INDEX, result.getFailingPaths() .get("file0")); assertEquals("unstaged modified file0", read(file0)); // index shall be unchanged assertEquals(indexState, indexState(CONTENT)); assertEquals(RepositoryState.SAFE, db.getRepositoryState()); } private int countPicks() throws IOException { int count = 0; File todoFile = new File(db.getDirectory(), "rebase-merge/git-rebase-todo"); BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(todoFile), "UTF-8")); try { String line = br.readLine(); while (line != null) { String actionToken = line.substring(0, line.indexOf(' ')); Action action = null; try { action = Action.parse(actionToken); } catch (Exception e) { // ignore } if (action != null) count++; line = br.readLine(); } return count; } finally { br.close(); } } @Test public void testFastForwardWithMultipleCommitsOnDifferentBranches() throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); RevCommit first = git.commit().setMessage("Add file1").call(); assertTrue(new File(db.getWorkTree(), FILE1).exists()); // create a topic branch createBranch(first, "refs/heads/topic"); // create file2 on master writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); RevCommit second = git.commit().setMessage("Add file2").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); // create side branch createBranch(second, "refs/heads/side"); // update FILE1 on master writeTrashFile(FILE1, "blah"); git.add().addFilepattern(FILE1).call(); git.commit().setMessage("updated file1 on master") .call(); // switch to side branch and update file2 checkoutBranch("refs/heads/side"); writeTrashFile("file2", "more change"); git.add().addFilepattern("file2").call(); RevCommit fourth = git.commit().setMessage("update file2 on side") .call(); // switch back to master and merge in side checkoutBranch("refs/heads/master"); MergeResult result = git.merge().include(fourth.getId()) .setStrategy(MergeStrategy.RESOLVE).call(); assertEquals(MergeStatus.MERGED, result.getMergeStatus()); // switch back to topic branch and rebase it onto master checkoutBranch("refs/heads/topic"); RebaseResult res = git.rebase().setUpstream("refs/heads/master").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); checkFile(new File(db.getWorkTree(), "file2"), "more change"); assertEquals(Status.FAST_FORWARD, res.getStatus()); } @Test public void testRebaseShouldLeaveWorkspaceUntouchedWithUnstagedChangesConflict() throws Exception { writeTrashFile(FILE1, "initial file"); git.add().addFilepattern(FILE1).call(); RevCommit initial = git.commit().setMessage("initial commit").call(); createBranch(initial, "refs/heads/side"); writeTrashFile(FILE1, "updated file"); git.add().addFilepattern(FILE1).call(); git.commit().setMessage("updated FILE1 on master").call(); // switch to side, modify the file checkoutBranch("refs/heads/side"); writeTrashFile(FILE1, "side update"); git.add().addFilepattern(FILE1).call(); git.commit().setMessage("updated FILE1 on side").call(); File theFile = writeTrashFile(FILE1, "dirty the file"); // and attempt to rebase try { RebaseResult rebaseResult = git.rebase() .setUpstream("refs/heads/master").call(); fail("Checkout with conflict should have occured, not " + rebaseResult.getStatus()); } catch (JGitInternalException e) { checkFile(theFile, "dirty the file"); } assertEquals(RepositoryState.SAFE, git.getRepository() .getRepositoryState()); } }