blob: cf2dead5a44c7332864d88d46f228f5c1c47de40 [file] [log] [blame]
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* 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).
*
* <pre>
* A - B (master)
* \
* C - D - F (topic)
* \ /
* E - (side)
* </pre>
*/
// 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();
RevCommit origHead = 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));
assertEquals(origHead, db.readOrigHead());
}
@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());
}
}