| /* |
| * 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.RebaseCommand.Action; |
| import org.eclipse.jgit.api.RebaseCommand.Operation; |
| import org.eclipse.jgit.api.RebaseResult.Status; |
| 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.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 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 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); |
| 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("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 |
| } |
| } |
| |
| 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(); |
| } |
| } |
| } |