Package org.eclipse.jgit.api

Source Code of org.eclipse.jgit.api.RebaseCommandTest

/*
* Copyright (C) 2010, 2013 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.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
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 java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jgit.api.MergeResult.MergeStatus;
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
import org.eclipse.jgit.api.RebaseCommand.Operation;
import org.eclipse.jgit.api.RebaseResult.Status;
import org.eclipse.jgit.api.errors.InvalidRebaseStepException;
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.diff.DiffEntry;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.IllegalTodoFileModification;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RebaseTodoLine;
import org.eclipse.jgit.lib.RebaseTodoLine.Action;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.junit.Before;
import org.junit.Test;

public class RebaseCommandTest extends RepositoryTestCase {
  private static final String GIT_REBASE_TODO = "rebase-merge/git-rebase-todo";

  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.setRefLogMessage("checkout: moving to " + head.getName(),
        false);
    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();
    RevCommit second = 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());

    List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
        .getReverseEntries();
    List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
        .getReverseEntries();
    List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
        .getReverseEntries();
    assertEquals("rebase finished: returning to refs/heads/topic", headLog
        .get(0).getComment());
    assertEquals("checkout: moving from topic to " + second.getName(),
        headLog.get(1).getComment());
    assertEquals(2, masterLog.size());
    assertEquals(2, topicLog.size());
    assertEquals(
        "rebase finished: refs/heads/topic onto " + second.getName(),
        topicLog.get(0).getComment());
  }

  @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();
    RevCommit second = 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());

    List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
        .getReverseEntries();
    List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
        .getReverseEntries();
    List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
        .getReverseEntries();
    assertEquals("rebase finished: returning to refs/heads/topic", headLog
        .get(0).getComment());
    assertEquals("checkout: moving from topic to " + second.getName(),
        headLog.get(1).getComment());
    assertEquals(3, masterLog.size());
    assertEquals(2, topicLog.size());
    assertEquals(
        "rebase finished: refs/heads/topic onto " + second.getName(),
        topicLog.get(0).getComment());
  }

  /**
   * Create the following commits and then attempt to rebase topic onto
   * master. This will serialize the branches.
   *
   * <pre>
   * A - B (master)
   *   \
   *    C - D - F (topic)
   *     \      /
   *      E  -  (side)
   * </pre>
   *
   * into
   *
   * <pre>
   * A - B - (master)  C' - D' - E' (topic')
   *   \
   *    C - D - F (topic)
   *     \      /
   *      E  -  (side)
   * </pre>
   *
   * @throws Exception
   */
  @Test
  public void testRebaseShouldIgnoreMergeCommits()
      throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    RevCommit a = git.commit().setMessage("Add file1").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create a topic branch
    createBranch(a, "refs/heads/topic");

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    RevCommit b = git.commit().setMessage("updated file1 on master").call();

    checkoutBranch("refs/heads/topic");
    writeTrashFile("file3", "more changess");
    git.add().addFilepattern("file3").call();
    RevCommit c = git.commit()
        .setMessage("update file3 on topic").call();

    // create a branch from the topic commit
    createBranch(c, "refs/heads/side");

    // second commit on topic
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    RevCommit d = 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 e = git.commit().setMessage("update file2 on side")
        .call();

    // switch back to topic and merge in side, creating f
    checkoutBranch("refs/heads/topic");
    MergeResult result = git.merge().include(e.getId())
        .setStrategy(MergeStrategy.RESOLVE).call();
    assertEquals(MergeStatus.MERGED, result.getMergeStatus());
    RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
    assertEquals(Status.OK, res.getStatus());

    RevWalk rw = new RevWalk(db);
    rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic")));
    assertDerivedFrom(rw.next(), e);
    assertDerivedFrom(rw.next(), d);
    assertDerivedFrom(rw.next(), c);
    assertEquals(b, rw.next());
    assertEquals(a, rw.next());

    List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
        .getReverseEntries();
    List<ReflogEntry> sideLog = db.getReflogReader("refs/heads/side")
        .getReverseEntries();
    List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
        .getReverseEntries();
    List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
        .getReverseEntries();
    assertEquals("rebase finished: returning to refs/heads/topic", headLog
        .get(0).getComment());
    assertEquals("rebase: update file2 on side", headLog.get(1)
        .getComment());
    assertEquals("rebase: Add file2", headLog.get(2).getComment());
    assertEquals("rebase: update file3 on topic", headLog.get(3)
        .getComment());
    assertEquals("checkout: moving from topic to " + b.getName(), headLog
        .get(4).getComment());
    assertEquals(2, masterLog.size());
    assertEquals(2, sideLog.size());
    assertEquals(5, topicLog.size());
    assertEquals("rebase finished: refs/heads/topic onto " + b.getName(),
        topicLog.get(0).getComment());
  }

  static void assertDerivedFrom(RevCommit derived, RevCommit original) {
    assertThat(derived, not(equalTo(original)));
    assertEquals(original.getFullMessage(), derived.getFullMessage());
  }

  @Test
  public void testRebasePreservingMerges1() throws Exception {
    doTestRebasePreservingMerges(true);
  }

  @Test
  public void testRebasePreservingMerges2() throws Exception {
    doTestRebasePreservingMerges(false);
  }

  /**
   * Transforms the same before-state as in
   * {@link #testRebaseShouldIgnoreMergeCommits()} to the following.
   * <p>
   * This test should always rewrite E.
   *
   * <pre>
   * A - B (master) - - -  C' - D' - F' (topic')
   *   \                    \       /
   *    C - D - F (topic)      - E'
   *     \     /
   *       - E (side)
   * </pre>
   *
   * @param testConflict
   * @throws Exception
   */
  private void doTestRebasePreservingMerges(boolean testConflict)
      throws Exception {
    RevWalk rw = new RevWalk(db);

    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    RevCommit a = git.commit().setMessage("commit a").call();

    // create a topic branch
    createBranch(a, "refs/heads/topic");

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    writeTrashFile("conflict", "b");
    git.add().addFilepattern(".").call();
    RevCommit b = git.commit().setMessage("commit b").call();

    checkoutBranch("refs/heads/topic");
    writeTrashFile("file3", "more changess");
    git.add().addFilepattern("file3").call();
    RevCommit c = git.commit().setMessage("commit c").call();

    // create a branch from the topic commit
    createBranch(c, "refs/heads/side");

    // second commit on topic
    writeTrashFile("file2", "file2");
    if (testConflict)
      writeTrashFile("conflict", "d");
    git.add().addFilepattern(".").call();
    RevCommit d = git.commit().setMessage("commit d").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // switch to side branch and update file2
    checkoutBranch("refs/heads/side");
    writeTrashFile("file3", "more change");
    if (testConflict)
      writeTrashFile("conflict", "e");
    git.add().addFilepattern(".").call();
    RevCommit e = git.commit().setMessage("commit e").call();

    // switch back to topic and merge in side, creating f
    checkoutBranch("refs/heads/topic");
    MergeResult result = git.merge().include(e.getId())
        .setStrategy(MergeStrategy.RESOLVE).call();
    final RevCommit f;
    if (testConflict) {
      assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
      assertEquals(Collections.singleton("conflict"), git.status().call()
          .getConflicting());
      // resolve
      writeTrashFile("conflict", "f resolved");
      git.add().addFilepattern("conflict").call();
      f = git.commit().setMessage("commit f").call();
    } else {
      assertEquals(MergeStatus.MERGED, result.getMergeStatus());
      f = rw.parseCommit(result.getNewHead());
    }

    RebaseResult res = git.rebase().setUpstream("refs/heads/master")
        .setPreserveMerges(true).call();
    if (testConflict) {
      // first there is a conflict whhen applying d
      assertEquals(Status.STOPPED, res.getStatus());
      assertEquals(Collections.singleton("conflict"), git.status().call()
          .getConflicting());
      assertTrue(read("conflict").contains("\nb\n=======\nd\n"));
      // resolve
      writeTrashFile("conflict", "d new");
      git.add().addFilepattern("conflict").call();
      res = git.rebase().setOperation(Operation.CONTINUE).call();

      // then there is a conflict when applying e
      assertEquals(Status.STOPPED, res.getStatus());
      assertEquals(Collections.singleton("conflict"), git.status().call()
          .getConflicting());
      assertTrue(read("conflict").contains("\nb\n=======\ne\n"));
      // resolve
      writeTrashFile("conflict", "e new");
      git.add().addFilepattern("conflict").call();
      res = git.rebase().setOperation(Operation.CONTINUE).call();

      // finally there is a conflict merging e'
      assertEquals(Status.STOPPED, res.getStatus());
      assertEquals(Collections.singleton("conflict"), git.status().call()
          .getConflicting());
      assertTrue(read("conflict").contains("\nd new\n=======\ne new\n"));
      // resolve
      writeTrashFile("conflict", "f new resolved");
      git.add().addFilepattern("conflict").call();
      res = git.rebase().setOperation(Operation.CONTINUE).call();
    }
    assertEquals(Status.OK, res.getStatus());

    if (testConflict)
      assertEquals("f new resolved", read("conflict"));
    assertEquals("blah", read(FILE1));
    assertEquals("file2", read("file2"));
    assertEquals("more change", read("file3"));

    rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic")));
    RevCommit newF = rw.next();
    assertDerivedFrom(newF, f);
    assertEquals(2, newF.getParentCount());
    RevCommit newD = rw.next();
    assertDerivedFrom(newD, d);
    if (testConflict)
      assertEquals("d new", readFile("conflict", newD));
    RevCommit newE = rw.next();
    assertDerivedFrom(newE, e);
    if (testConflict)
      assertEquals("e new", readFile("conflict", newE));
    assertEquals(newD, newF.getParent(0));
    assertEquals(newE, newF.getParent(1));
    assertDerivedFrom(rw.next(), c);
    assertEquals(b, rw.next());
    assertEquals(a, rw.next());
  }

  private String readFile(String path, RevCommit commit) throws IOException {
    TreeWalk walk = TreeWalk.forPath(db, path, commit.getTree());
    ObjectLoader loader = db.open(walk.getObjectId(0), Constants.OBJ_BLOB);
    String result = RawParseUtils.decode(loader.getCachedBytes());
    walk.release();
    return result;
  }

  @Test
  public void testRebasePreservingMergesWithUnrelatedSide1() throws Exception {
    doTestRebasePreservingMergesWithUnrelatedSide(true);
  }

  @Test
  public void testRebasePreservingMergesWithUnrelatedSide2() throws Exception {
    doTestRebasePreservingMergesWithUnrelatedSide(false);
  }

  /**
   * Rebase topic onto master, not rewriting E. The merge resulting in D is
   * confliicting to show that the manual merge resolution survives the
   * rebase.
   *
   * <pre>
   * A - B - G (master)
   *  \   \
   *   \   C - D - F (topic)
   *    \     /
   *      E (side)
   * </pre>
   *
   * <pre>
   * A - B - G (master)
   *  \       \
   *   \       C' - D' - F' (topic')
   *    \          /
   *      E (side)
   * </pre>
   *
   * @param testConflict
   * @throws Exception
   */
  private void doTestRebasePreservingMergesWithUnrelatedSide(
      boolean testConflict) throws Exception {
    RevWalk rw = new RevWalk(db);
    rw.sort(RevSort.TOPO);

    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    RevCommit a = git.commit().setMessage("commit a").call();

    writeTrashFile("file2", "blah");
    git.add().addFilepattern("file2").call();
    RevCommit b = git.commit().setMessage("commit b").call();

    // create a topic branch
    createBranch(b, "refs/heads/topic");
    checkoutBranch("refs/heads/topic");

    writeTrashFile("file3", "more changess");
    writeTrashFile(FILE1, "preparing conflict");
    git.add().addFilepattern("file3").addFilepattern(FILE1).call();
    RevCommit c = git.commit().setMessage("commit c").call();

    createBranch(a, "refs/heads/side");
    checkoutBranch("refs/heads/side");
    writeTrashFile("conflict", "e");
    writeTrashFile(FILE1, FILE1 + "\n" + "line 2");
    git.add().addFilepattern(".").call();
    RevCommit e = git.commit().setMessage("commit e").call();

    // switch back to topic and merge in side, creating d
    checkoutBranch("refs/heads/topic");
    MergeResult result = git.merge().include(e)
        .setStrategy(MergeStrategy.RESOLVE).call();

    assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
    assertEquals(result.getConflicts().keySet(),
        Collections.singleton(FILE1));
    writeTrashFile(FILE1, "merge resolution");
    git.add().addFilepattern(FILE1).call();
    RevCommit d = git.commit().setMessage("commit d").call();

    RevCommit f = commitFile("file2", "new content two", "topic");

    checkoutBranch("refs/heads/master");
    writeTrashFile("fileg", "fileg");
    if (testConflict)
      writeTrashFile("conflict", "g");
    git.add().addFilepattern(".").call();
    RevCommit g = git.commit().setMessage("commit g").call();

    checkoutBranch("refs/heads/topic");
    RebaseResult res = git.rebase().setUpstream("refs/heads/master")
        .setPreserveMerges(true).call();
    if (testConflict) {
      assertEquals(Status.STOPPED, res.getStatus());
      assertEquals(Collections.singleton("conflict"), git.status().call()
          .getConflicting());
      // resolve
      writeTrashFile("conflict", "e");
      git.add().addFilepattern("conflict").call();
      res = git.rebase().setOperation(Operation.CONTINUE).call();
    }
    assertEquals(Status.OK, res.getStatus());

    assertEquals("merge resolution", read(FILE1));
    assertEquals("new content two", read("file2"));
    assertEquals("more changess", read("file3"));
    assertEquals("fileg", read("fileg"));

    rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic")));
    RevCommit newF = rw.next();
    assertDerivedFrom(newF, f);
    RevCommit newD = rw.next();
    assertDerivedFrom(newD, d);
    assertEquals(2, newD.getParentCount());
    RevCommit newC = rw.next();
    assertDerivedFrom(newC, c);
    RevCommit newE = rw.next();
    assertEquals(e, newE);
    assertEquals(newC, newD.getParent(0));
    assertEquals(e, newD.getParent(1));
    assertEquals(g, rw.next());
    assertEquals(b, rw.next());
    assertEquals(a, rw.next());
  }

  @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());

    assertEquals(2, db.getReflogReader(Constants.HEAD).getReverseEntries()
        .size());
    assertEquals(2, db.getReflogReader("refs/heads/master")
        .getReverseEntries().size());
  }

  @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());

    assertEquals(1, db.getReflogReader(Constants.HEAD).getReverseEntries()
        .size());
    assertEquals(1, db.getReflogReader("refs/heads/master")
        .getReverseEntries().size());
  }

  @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());
    List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
        .getReverseEntries();
    List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
        .getReverseEntries();
    List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
        .getReverseEntries();
    assertEquals(2, masterLog.size());
    assertEquals(3, topicLog.size());
    assertEquals("rebase finished: refs/heads/topic onto "
        + lastMasterChange.getName(), topicLog.get(0).getComment());
    assertEquals("rebase finished: returning to refs/heads/topic", headLog
        .get(0).getComment());
  }

  @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));

    List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
        .getReverseEntries();
    assertEquals(8, headLog.size());
    assertEquals("rebase: change file1 in topic", headLog.get(0)
        .getComment());
    assertEquals("checkout: moving from " + topicCommit.getName() + " to "
        + lastMasterChange.getName(), headLog.get(1).getComment());
  }

  @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,
        "<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
        ">>>>>>> e0d1dea change file1 in topic\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 testStopOnConflictAndAbortWithDetachedHEAD() 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");

    git.checkout().setName(lastTopicCommit.getName()).call();

    RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
    assertEquals(Status.STOPPED, res.getStatus());
    assertEquals(conflicting, res.getCurrentCommit());
    checkFile(FILE1,
        "<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
        ">>>>>>> e0d1dea change file1 in topic\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(lastTopicCommit.getName(), 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<<<<<<< Upstream, based on master\n3master\n=======\n3topic",
        ">>>>>>> 5afc8df change file1 in topic again\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
  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
    RebaseResult result = git.rebase().setUpstream("refs/heads/master")
        .call();
    assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
    assertEquals(1, result.getUncommittedChanges().size());
    assertEquals("file2", result.getUncommittedChanges().get(0));
  }

  @Test
  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

    RebaseResult result = git.rebase().setUpstream("refs/heads/master")
        .call();
    assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
    assertEquals(1, result.getUncommittedChanges().size());
    assertEquals("file2", result.getUncommittedChanges().get(0));

    checkFile(uncommittedFile, "uncommitted file2");
    assertEquals(RepositoryState.SAFE, git.getRepository().getRepositoryState());
  }

  @Test
  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
    RebaseResult result = git.rebase().setUpstream("refs/heads/master")
        .call();
    assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
    assertEquals(1, result.getUncommittedChanges().size());
    assertEquals(FILE1, result.getUncommittedChanges().get(0));
  }

  @Test
  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
    RebaseResult result = git.rebase().setUpstream("refs/heads/master")
        .call();
    assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
    assertEquals(1, result.getUncommittedChanges().size());
    assertEquals(FILE1, result.getUncommittedChanges().get(0));
  }

  @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.UNCOMMITTED_CHANGES,
        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.UNCOMMITTED_CHANGES, result.getStatus());
    assertEquals(1, result.getUncommittedChanges().size());
    // 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.UNCOMMITTED_CHANGES,
        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.UNCOMMITTED_CHANGES, result.getStatus());
    // staged file0 causes DIRTY_INDEX
    assertEquals(1, result.getUncommittedChanges().size());
    assertEquals("unstaged modified file0", read(file0));
    // index shall be unchanged
    assertEquals(indexState, indexState(CONTENT));
    assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  }

  @Test
  public void testFastForwardRebaseWithModification() 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
    createBranch(commit, "refs/heads/topic");

    // still on master / modify file1, add and commit
    writeTrashFile(FILE1, "modified file1");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("commit2").call();

    // checkout topic branch / modify file0 and add to index
    checkoutBranch("refs/heads/topic");
    writeTrashFile("file0", "modified file0 in index");
    git.add().addFilepattern("file0").addFilepattern(FILE1).call();
    // modify once more
    writeTrashFile("file0", "modified file0");

    // rebase
    RebaseResult result = git.rebase().setUpstream("refs/heads/master")
        .call();
    assertEquals(Status.FAST_FORWARD, result.getStatus());
    checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
    checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
    assertEquals("[file0, mode:100644, content:modified file0 in index]"
        + "[file1, mode:100644, content:modified file1]",
        indexState(CONTENT));
    assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  }

  @Test
  public void testRebaseWithModificationShouldNotDeleteData()
      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
    createBranch(commit, "refs/heads/topic");

    // still on master / modify file1, add and commit
    writeTrashFile(FILE1, "modified file1");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("commit2").call();

    // checkout topic branch / modify file1, add and commit
    checkoutBranch("refs/heads/topic");
    writeTrashFile(FILE1, "modified file1 on topic");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("commit3").call();

    writeTrashFile("file0", "modified file0");

    RebaseResult result = git.rebase().setUpstream("refs/heads/master")
        .call();
    // the following condition was true before commit 83b6ab233:
    // jgit started the rebase and deleted the change on abort
    // This test should verify that content was deleted
    if (result.getStatus() == Status.STOPPED)
      git.rebase().setOperation(Operation.ABORT).call();

    checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
    checkFile(new File(db.getWorkTree(), FILE1),
        "modified file1 on topic");
    assertEquals("[file0, mode:100644, content:file0]"
        + "[file1, mode:100644, content:modified file1 on topic]",
        indexState(CONTENT));
  }

  @Test
  public void testRebaseWithUncommittedDelete() 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
    createBranch(commit, "refs/heads/topic");

    // still on master / modify file1, add and commit
    writeTrashFile(FILE1, "modified file1");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("commit2").call();

    // checkout topic branch / delete file0 and add to index
    checkoutBranch("refs/heads/topic");
    git.rm().addFilepattern("file0").call();
    // do not commit

    // rebase
    RebaseResult result = git.rebase().setUpstream("refs/heads/master")
        .call();
    assertEquals(Status.FAST_FORWARD, result.getStatus());
    assertFalse("File should still be deleted", file0.exists());
    // index should only have updated file1
    assertEquals("[file1, mode:100644, content:modified file1]",
        indexState(CONTENT));
    assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  }

  @Test
  public void testRebaseWithAutoStash()
      throws Exception {
    // create file0, add and commit
    db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
        ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
    writeTrashFile("file0", "file0");
    git.add().addFilepattern("file0").call();
    git.commit().setMessage("commit0").call();
    // create file1, add and commit
    writeTrashFile(FILE1, "file1");
    git.add().addFilepattern(FILE1).call();
    RevCommit commit = git.commit().setMessage("commit1").call();

    // create topic branch 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());
    checkFile(new File(db.getWorkTree(), "file0"),
        "unstaged modified file0");
    checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
    checkFile(new File(db.getWorkTree(), "file2"), "file2");
    assertEquals("[file0, mode:100644, content:file0]"
        + "[file1, mode:100644, content:modified file1]"
        + "[file2, mode:100644, content:file2]",
        indexState(CONTENT));
    assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  }

  @Test
  public void testRebaseWithAutoStashConflictOnApply() throws Exception {
    // create file0, add and commit
    db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
        ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
    writeTrashFile("file0", "file0");
    git.add().addFilepattern("file0").call();
    git.commit().setMessage("commit0").call();
    // create file1, add and commit
    writeTrashFile(FILE1, "file1");
    git.add().addFilepattern(FILE1).call();
    RevCommit commit = git.commit().setMessage("commit1").call();

    // create topic branch 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("file1", "unstaged modified file1");

    // rebase
    assertEquals(Status.STASH_APPLY_CONFLICTS,
        git.rebase().setUpstream("refs/heads/master").call()
            .getStatus());
    checkFile(new File(db.getWorkTree(), "file0"), "file0");
    checkFile(
        new File(db.getWorkTree(), FILE1),
        "<<<<<<< HEAD\nmodified file1\n=======\nunstaged modified file1\n>>>>>>> stash\n");
    checkFile(new File(db.getWorkTree(), "file2"), "file2");
    assertEquals(
        "[file0, mode:100644, content:file0]"
            + "[file1, mode:100644, stage:1, content:file1]"
            + "[file1, mode:100644, stage:2, content:modified file1]"
            + "[file1, mode:100644, stage:3, content:unstaged modified file1]"
            + "[file2, mode:100644, content:file2]",
        indexState(CONTENT));
    assertEquals(RepositoryState.SAFE, db.getRepositoryState());

    List<DiffEntry> diffs = getStashedDiff();
    assertEquals(1, diffs.size());
    assertEquals(DiffEntry.ChangeType.MODIFY, diffs.get(0).getChangeType());
    assertEquals("file1", diffs.get(0).getOldPath());
  }

  @Test
  public void testFastForwardRebaseWithAutoStash() throws Exception {
    // create file0, add and commit
    db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
        ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
    writeTrashFile("file0", "file0");
    git.add().addFilepattern("file0").call();
    git.commit().setMessage("commit0").call();
    // create file1, add and commit
    writeTrashFile(FILE1, "file1");
    git.add().addFilepattern(FILE1).call();
    RevCommit commit = git.commit().setMessage("commit1").call();

    // create topic branch
    createBranch(commit, "refs/heads/topic");

    // checkout master branch / modify file1, add and commit
    checkoutBranch("refs/heads/master");
    writeTrashFile(FILE1, "modified file1");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("commit3").call();

    // checkout topic branch / modify file0
    checkoutBranch("refs/heads/topic");
    writeTrashFile("file0", "unstaged modified file0");

    // rebase
    assertEquals(Status.FAST_FORWARD,
        git.rebase().setUpstream("refs/heads/master")
        .call().getStatus());
    checkFile(new File(db.getWorkTree(), "file0"),
        "unstaged modified file0");
    checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
    assertEquals("[file0, mode:100644, content:file0]"
        + "[file1, mode:100644, content:modified file1]",
        indexState(CONTENT));
    assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  }

  private List<DiffEntry> getStashedDiff() throws AmbiguousObjectException,
      IncorrectObjectTypeException, IOException, MissingObjectException {
    ObjectId stashId = db.resolve("stash@{0}");
    RevWalk revWalk = new RevWalk(db);
    RevCommit stashCommit = revWalk.parseCommit(stashId);
    List<DiffEntry> diffs = diffWorkingAgainstHead(stashCommit, revWalk);
    return diffs;
  }

  private TreeWalk createTreeWalk() {
    TreeWalk walk = new TreeWalk(db);
    walk.setRecursive(true);
    walk.setFilter(TreeFilter.ANY_DIFF);
    return walk;
  }

  private List<DiffEntry> diffWorkingAgainstHead(final RevCommit commit,
      RevWalk revWalk)
      throws IOException {
    TreeWalk walk = createTreeWalk();
    RevCommit parentCommit = revWalk.parseCommit(commit.getParent(0));
    try {
      walk.addTree(parentCommit.getTree());
      walk.addTree(commit.getTree());
      return DiffEntry.scan(walk);
    } finally {
      walk.release();
    }
  }

  private int countPicks() throws IOException {
    int count = 0;
    File todoFile = getTodoFile();
    BufferedReader br = new BufferedReader(new InputStreamReader(
        new FileInputStream(todoFile), "UTF-8"));
    try {
      String line = br.readLine();
      while (line != null) {
        int firstBlank = line.indexOf(' ');
        if (firstBlank != -1) {
          String actionToken = line.substring(0, firstBlank);
          Action action = null;
          try {
            action = Action.parse(actionToken);
          } catch (Exception e) {
            // ignore
          }
          if (Action.PICK.equals(action))
            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
    RebaseResult rebaseResult = git.rebase()
          .setUpstream("refs/heads/master").call();
    assertEquals(Status.UNCOMMITTED_CHANGES, rebaseResult.getStatus());
    assertEquals(1, rebaseResult.getUncommittedChanges().size());
    assertEquals(FILE1, rebaseResult.getUncommittedChanges().get(0));

    checkFile(theFile, "dirty the file");

    assertEquals(RepositoryState.SAFE, git.getRepository()
        .getRepositoryState());
  }

  @Test
  public void testAbortShouldAlsoAbortNonInteractiveRebaseWithRebaseApplyDir()
      throws Exception {
    writeTrashFile(FILE1, "initial file");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("initial commit").call();

    File applyDir = new File(db.getDirectory(), "rebase-apply");
    File headName = new File(applyDir, "head-name");
    FileUtils.mkdir(applyDir);
    write(headName, "master");
    db.writeOrigHead(db.resolve(Constants.HEAD));

    git.rebase().setOperation(Operation.ABORT).call();

    assertFalse("Abort should clean up .git/rebase-apply",
        applyDir.exists());
    assertEquals(RepositoryState.SAFE, git.getRepository()
        .getRepositoryState());
  }

  @Test
  public void testRebaseShouldBeAbleToHandleEmptyLinesInRebaseTodoFile()
      throws IOException {
    String emptyLine = "\n";
    String todo = "pick 1111111 Commit 1\n" + emptyLine
        + "pick 2222222 Commit 2\n" + emptyLine
        + "# Comment line at end\n";
    write(getTodoFile(), todo);

    List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);
    assertEquals(2, steps.size());
    assertEquals("1111111", steps.get(0).getCommit().name());
    assertEquals("2222222", steps.get(1).getCommit().name());
  }

  @Test
  public void testRebaseShouldBeAbleToHandleLinesWithoutCommitMessageInRebaseTodoFile()
      throws IOException {
    String todo = "pick 1111111 \n" + "pick 2222222 Commit 2\n"
        + "# Comment line at end\n";
    write(getTodoFile(), todo);

    List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);
    assertEquals(2, steps.size());
    assertEquals("1111111", steps.get(0).getCommit().name());
    assertEquals("2222222", steps.get(1).getCommit().name());
  }

  @Test
  public void testRebaseShouldNotFailIfUserAddCommentLinesInPrepareSteps()
      throws Exception {
    commitFile(FILE1, FILE1, "master");
    RevCommit c2 = commitFile("file2", "file2", "master");

    // update files on master
    commitFile(FILE1, "blah", "master");
    RevCommit c4 = commitFile("file2", "more change", "master");

    RebaseResult res = git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {
          public void prepareSteps(List<RebaseTodoLine> steps) {
            steps.add(0, new RebaseTodoLine(
                "# Comment that should not be processed"));
          }

          public String modifyCommitMessage(String commit) {
            fail("modifyCommitMessage() was not expected to be called");
            return commit;
          }
        }).call();

    assertEquals(RebaseResult.Status.FAST_FORWARD, res.getStatus());

    RebaseResult res2 = git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {
          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              // delete RevCommit c4
              steps.get(0).setAction(Action.COMMENT);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            fail("modifyCommitMessage() was not expected to be called");
            return commit;
          }
        }).call();

    assertEquals(RebaseResult.Status.OK, res2.getStatus());

    ObjectId headId = db.resolve(Constants.HEAD);
    RevWalk rw = new RevWalk(db);
    RevCommit rc = rw.parseCommit(headId);

    ObjectId head1Id = db.resolve(Constants.HEAD + "~1");
    RevCommit rc1 = rw.parseCommit(head1Id);

    assertEquals(rc.getFullMessage(), c4.getFullMessage());
    assertEquals(rc1.getFullMessage(), c2.getFullMessage());
  }

  @Test
  public void testParseRewordCommand() throws Exception {
    String todo = "pick 1111111 Commit 1\n"
        + "reword 2222222 Commit 2\n";
    write(getTodoFile(), todo);

    List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);

    assertEquals(2, steps.size());
    assertEquals("1111111", steps.get(0).getCommit().name());
    assertEquals("2222222", steps.get(1).getCommit().name());
    assertEquals(Action.REWORD, steps.get(1).getAction());
  }

  @Test
  public void testEmptyRebaseTodo() throws Exception {
    write(getTodoFile(), "");
    assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, true).size());
    assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
  }

  @Test
  public void testOnlyCommentRebaseTodo() throws Exception {
    write(getTodoFile(), "# a b c d e\n# e f");
    assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
    List<RebaseTodoLine> lines = db.readRebaseTodo(GIT_REBASE_TODO, true);
    assertEquals(2, lines.size());
    for (RebaseTodoLine line : lines)
      assertEquals(Action.COMMENT, line.getAction());
    write(getTodoFile(), "# a b c d e\n# e f\n");
    assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
    lines = db.readRebaseTodo(GIT_REBASE_TODO, true);
    assertEquals(2, lines.size());
    for (RebaseTodoLine line : lines)
      assertEquals(Action.COMMENT, line.getAction());
    write(getTodoFile(), "    \r\n# a b c d e\r\n# e f\r\n#");
    assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
    lines = db.readRebaseTodo(GIT_REBASE_TODO, true);
    assertEquals(4, lines.size());
    for (RebaseTodoLine line : lines)
      assertEquals(Action.COMMENT, line.getAction());
  }

  @Test
  public void testLeadingSpacesRebaseTodo() throws Exception {
    String todo =  "  \t\t pick 1111111 Commit 1\n"
          + "\t\n"
          + "\treword 2222222 Commit 2\n";
    write(getTodoFile(), todo);

    List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);

    assertEquals(2, steps.size());
    assertEquals("1111111", steps.get(0).getCommit().name());
    assertEquals("2222222", steps.get(1).getCommit().name());
    assertEquals(Action.REWORD, steps.get(1).getAction());
  }

  @Test
  public void testRebaseShouldTryToParseValidLineMarkedAsComment()
      throws IOException {
    String todo = "# pick 1111111 Valid line commented out with space\n"
        + "#edit 2222222 Valid line commented out without space\n"
        + "# pick invalidLine Comment line at end\n";
    write(getTodoFile(), todo);

    List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, true);
    assertEquals(3, steps.size());

    RebaseTodoLine firstLine = steps.get(0);

    assertEquals("1111111", firstLine.getCommit().name());
    assertEquals("Valid line commented out with space",
        firstLine.getShortMessage());
    assertEquals("comment", firstLine.getAction().toToken());

    try {
      firstLine.setAction(Action.PICK);
      assertEquals("1111111", firstLine.getCommit().name());
      assertEquals("pick", firstLine.getAction().toToken());
    } catch (Exception e) {
      fail("Valid parsable RebaseTodoLine that has been commented out should allow to change the action, but failed");
    }

    assertEquals("2222222", steps.get(1).getCommit().name());
    assertEquals("comment", steps.get(1).getAction().toToken());

    assertEquals(null, steps.get(2).getCommit());
    assertEquals(null, steps.get(2).getShortMessage());
    assertEquals("comment", steps.get(2).getAction().toToken());
    assertEquals("# pick invalidLine Comment line at end", steps.get(2)
        .getComment());
    try {
      steps.get(2).setAction(Action.PICK);
      fail("A comment RebaseTodoLine that doesn't contain a valid parsable line should fail, but doesn't");
    } catch (Exception e) {
      // expected
    }

  }

  @SuppressWarnings("unused")
  @Test
  public void testRebaseTodoLineSetComment() throws Exception {
    try {
      new RebaseTodoLine("This is a invalid comment");
      fail("Constructing a comment line with invalid comment string should fail, but doesn't");
    } catch (IllegalArgumentException e) {
      // expected
    }
    RebaseTodoLine validCommentLine = new RebaseTodoLine(
        "# This is a comment");
    assertEquals(Action.COMMENT, validCommentLine.getAction());
    assertEquals("# This is a comment", validCommentLine.getComment());

    RebaseTodoLine actionLineToBeChanged = new RebaseTodoLine(Action.EDIT,
        AbbreviatedObjectId.fromString("1111111"), "short Message");
    assertEquals(null, actionLineToBeChanged.getComment());

    try {
      actionLineToBeChanged.setComment("invalid comment");
      fail("Setting a invalid comment string should fail but doesn't");
    } catch (IllegalArgumentException e) {
      assertEquals(null, actionLineToBeChanged.getComment());
    }

    actionLineToBeChanged.setComment("# valid comment");
    assertEquals("# valid comment", actionLineToBeChanged.getComment());
    try {
      actionLineToBeChanged.setComment("invalid comment");
      fail("Setting a invalid comment string should fail but doesn't");
    } catch (IllegalArgumentException e) {
      // expected
      // setting comment failed, but was successfully set before,
      // therefore it may not be altered since then
      assertEquals("# valid comment", actionLineToBeChanged.getComment());
    }
    try {
      actionLineToBeChanged.setComment("# line1 \n line2");
      actionLineToBeChanged.setComment("line1 \n line2");
      actionLineToBeChanged.setComment("\n");
      actionLineToBeChanged.setComment("# line1 \r line2");
      actionLineToBeChanged.setComment("line1 \r line2");
      actionLineToBeChanged.setComment("\r");
      actionLineToBeChanged.setComment("# line1 \n\r line2");
      actionLineToBeChanged.setComment("line1 \n\r line2");
      actionLineToBeChanged.setComment("\n\r");
      fail("Setting a multiline comment string should fail but doesn't");
    } catch (IllegalArgumentException e) {
      // expected
    }
    // Try setting valid comments
    actionLineToBeChanged.setComment("# valid comment");
    assertEquals("# valid comment", actionLineToBeChanged.getComment());

    actionLineToBeChanged.setComment("# \t \t valid comment");
    assertEquals("# \t \t valid comment",
        actionLineToBeChanged.getComment());

    actionLineToBeChanged.setComment("#       ");
    assertEquals("#       ", actionLineToBeChanged.getComment());

    actionLineToBeChanged.setComment("");
    assertEquals("", actionLineToBeChanged.getComment());

    actionLineToBeChanged.setComment("  ");
    assertEquals("  ", actionLineToBeChanged.getComment());

    actionLineToBeChanged.setComment("\t\t");
    assertEquals("\t\t", actionLineToBeChanged.getComment());

    actionLineToBeChanged.setComment(null);
    assertEquals(null, actionLineToBeChanged.getComment());
  }

  @Test
  public void testRebaseInteractiveReword() 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());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("updated file1 on master").call();

    writeTrashFile("file2", "more change");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("update file2 on side").call();

    RebaseResult res = git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.REWORD);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return "rewritten commit message";
          }
        }).call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());
    checkFile(new File(db.getWorkTree(), "file2"), "more change");
    assertEquals(Status.OK, res.getStatus());
    Iterator<RevCommit> logIterator = git.log().all().call().iterator();
    logIterator.next(); // skip first commit;
    String actualCommitMag = logIterator.next().getShortMessage();
    assertEquals("rewritten commit message", actualCommitMag);
  }

  @Test
  public void testRebaseInteractiveEdit() 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());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("updated file1 on master").call();

    writeTrashFile("file2", "more change");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("update file2 on side").call();

    RebaseResult res = git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {
          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.EDIT);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return ""; // not used
          }
        }).call();
    assertEquals(Status.EDIT, res.getStatus());
    RevCommit toBeEditted = git.log().call().iterator().next();
    assertEquals("updated file1 on master", toBeEditted.getFullMessage());

    // change file and commit with new commit message
    writeTrashFile("file1", "edited");
    git.commit().setAll(true).setAmend(true)
        .setMessage("edited commit message").call();
    // resume rebase
    res = git.rebase().setOperation(Operation.CONTINUE).call();

    checkFile(new File(db.getWorkTree(), "file1"), "edited");
    assertEquals(Status.OK, res.getStatus());
    Iterator<RevCommit> logIterator = git.log().all().call().iterator();
    logIterator.next(); // skip first commit;
    String actualCommitMag = logIterator.next().getShortMessage();
    assertEquals("edited commit message", actualCommitMag);
  }

  @Test
  public void testParseSquashFixupSequenceCount() {
    int count = RebaseCommand
        .parseSquashFixupSequenceCount("# This is a combination of 3 commits.\n# newline");
    assertEquals(3, count);
  }

  @Test
  public void testRebaseInteractiveSingleSquashAndModifyMessage() throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("updated file1 on master\nnew line").call();

    writeTrashFile("file2", "more change");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("update file2 on master\nnew line").call();

    git.rebase().setUpstream("HEAD~3")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(1).setAction(Action.SQUASH);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            final File messageSquashFile = new File(db
                .getDirectory(), "rebase-merge/message-squash");
            final File messageFixupFile = new File(db
                .getDirectory(), "rebase-merge/message-fixup");

            assertFalse(messageFixupFile.exists());
            assertTrue(messageSquashFile.exists());
            assertEquals(
                "# This is a combination of 2 commits.\n# The first commit's message is:\nAdd file2\nnew line\n# This is the 2nd commit message:\nupdated file1 on master\nnew line",
                commit);

            try {
              byte[] messageSquashBytes = IO
                  .readFully(messageSquashFile);
              int end = RawParseUtils.prevLF(messageSquashBytes,
                  messageSquashBytes.length);
              String messageSquashContent = RawParseUtils.decode(
                  messageSquashBytes, 0, end + 1);
              assertEquals(messageSquashContent, commit);
            } catch (Throwable t) {
              fail(t.getMessage());
            }

            return "changed";
          }
        }).call();

    RevWalk walk = new RevWalk(db);
    ObjectId headId = db.resolve(Constants.HEAD);
    RevCommit headCommit = walk.parseCommit(headId);
    assertEquals(headCommit.getFullMessage(),
        "update file2 on master\nnew line");

    ObjectId head2Id = db.resolve(Constants.HEAD + "^1");
    RevCommit head1Commit = walk.parseCommit(head2Id);
    assertEquals("changed", head1Commit.getFullMessage());
  }

  @Test
  public void testRebaseInteractiveMultipleSquash() throws Exception {
    // create file0 on master
    writeTrashFile("file0", "file0");
    git.add().addFilepattern("file0").call();
    git.commit().setMessage("Add file0\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file0").exists());

    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("updated file1 on master\nnew line").call();

    writeTrashFile("file2", "more change");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("update file2 on master\nnew line").call();

    git.rebase().setUpstream("HEAD~4")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(1).setAction(Action.SQUASH);
              steps.get(2).setAction(Action.SQUASH);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            final File messageSquashFile = new File(db.getDirectory(),
                "rebase-merge/message-squash");
            final File messageFixupFile = new File(db.getDirectory(),
                "rebase-merge/message-fixup");
            assertFalse(messageFixupFile.exists());
            assertTrue(messageSquashFile.exists());
            assertEquals(
                "# This is a combination of 3 commits.\n# The first commit's message is:\nAdd file1\nnew line\n# This is the 2nd commit message:\nAdd file2\nnew line\n# This is the 3rd commit message:\nupdated file1 on master\nnew line",
                commit);

            try {
              byte[] messageSquashBytes = IO
                  .readFully(messageSquashFile);
              int end = RawParseUtils.prevLF(messageSquashBytes,
                  messageSquashBytes.length);
              String messageSquashContend = RawParseUtils.decode(
                  messageSquashBytes, 0, end + 1);
              assertEquals(messageSquashContend, commit);
            } catch (Throwable t) {
              fail(t.getMessage());
            }

            return "# This is a combination of 3 commits.\n# The first commit's message is:\nAdd file1\nnew line\n# This is the 2nd commit message:\nAdd file2\nnew line\n# This is the 3rd commit message:\nupdated file1 on master\nnew line";
          }
        }).call();

    RevWalk walk = new RevWalk(db);
    ObjectId headId = db.resolve(Constants.HEAD);
    RevCommit headCommit = walk.parseCommit(headId);
    assertEquals(headCommit.getFullMessage(),
        "update file2 on master\nnew line");

    ObjectId head2Id = db.resolve(Constants.HEAD + "^1");
    RevCommit head1Commit = walk.parseCommit(head2Id);
    assertEquals(
        "Add file1\nnew line\nAdd file2\nnew line\nupdated file1 on master\nnew line",
        head1Commit.getFullMessage());
  }

  @Test
  public void testRebaseInteractiveMixedSquashAndFixup() throws Exception {
    // create file0 on master
    writeTrashFile("file0", "file0");
    git.add().addFilepattern("file0").call();
    git.commit().setMessage("Add file0\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file0").exists());

    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("updated file1 on master\nnew line").call();

    writeTrashFile("file2", "more change");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("update file2 on master\nnew line").call();

    git.rebase().setUpstream("HEAD~4")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(1).setAction(Action.FIXUP);
              steps.get(2).setAction(Action.SQUASH);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            final File messageSquashFile = new File(db
                .getDirectory(), "rebase-merge/message-squash");
            final File messageFixupFile = new File(db
                .getDirectory(), "rebase-merge/message-fixup");

            assertFalse(messageFixupFile.exists());
            assertTrue(messageSquashFile.exists());
            assertEquals(
                "# This is a combination of 3 commits.\n# The first commit's message is:\nAdd file1\nnew line\n# The 2nd commit message will be skipped:\n# Add file2\n# new line\n# This is the 3rd commit message:\nupdated file1 on master\nnew line",
                commit);

            try {
              byte[] messageSquashBytes = IO
                  .readFully(messageSquashFile);
              int end = RawParseUtils.prevLF(messageSquashBytes,
                  messageSquashBytes.length);
              String messageSquashContend = RawParseUtils.decode(
                  messageSquashBytes, 0, end + 1);
              assertEquals(messageSquashContend, commit);
            } catch (Throwable t) {
              fail(t.getMessage());
            }

            return "changed";
          }
        }).call();

    RevWalk walk = new RevWalk(db);
    ObjectId headId = db.resolve(Constants.HEAD);
    RevCommit headCommit = walk.parseCommit(headId);
    assertEquals(headCommit.getFullMessage(),
        "update file2 on master\nnew line");

    ObjectId head2Id = db.resolve(Constants.HEAD + "^1");
    RevCommit head1Commit = walk.parseCommit(head2Id);
    assertEquals("changed", head1Commit.getFullMessage());
  }

  @Test
  public void testRebaseInteractiveSingleFixup() throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("updated file1 on master\nnew line").call();

    writeTrashFile("file2", "more change");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("update file2 on master\nnew line").call();

    git.rebase().setUpstream("HEAD~3")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(1).setAction(Action.FIXUP);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            fail("No callback to modify commit message expected for single fixup");
            return commit;
          }
        }).call();

    RevWalk walk = new RevWalk(db);
    ObjectId headId = db.resolve(Constants.HEAD);
    RevCommit headCommit = walk.parseCommit(headId);
    assertEquals("update file2 on master\nnew line",
        headCommit.getFullMessage());

    ObjectId head1Id = db.resolve(Constants.HEAD + "^1");
    RevCommit head1Commit = walk.parseCommit(head1Id);
    assertEquals("Add file2\nnew line",
        head1Commit.getFullMessage());
  }

  @Test
  public void testRebaseInteractiveFixupWithBlankLines() throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    // update FILE1 on master
    writeTrashFile(FILE1, "blah");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("updated file1 on master\n\nsome text").call();

    git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(1).setAction(Action.FIXUP);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            fail("No callback to modify commit message expected for single fixup");
            return commit;
          }
        }).call();

    RevWalk walk = new RevWalk(db);
    ObjectId headId = db.resolve(Constants.HEAD);
    RevCommit headCommit = walk.parseCommit(headId);
    assertEquals("Add file2",
        headCommit.getFullMessage());
  }

  @Test(expected = InvalidRebaseStepException.class)
  public void testRebaseInteractiveFixupFirstCommitShouldFail()
      throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    git.rebase().setUpstream("HEAD~1")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.FIXUP);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return commit;
          }
        }).call();
  }

  @Test(expected = InvalidRebaseStepException.class)
  public void testRebaseInteractiveSquashFirstCommitShouldFail()
      throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    git.rebase().setUpstream("HEAD~1")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.SQUASH);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return commit;
          }
        }).call();
  }

  @Test
  public void testRebaseEndsIfLastStepIsEdit() throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // create file2 on master
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Add file2\nnew line").call();
    assertTrue(new File(db.getWorkTree(), "file2").exists());

    git.rebase().setUpstream("HEAD~1")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.EDIT);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return commit;
          }
        }).call();
    git.commit().setAmend(true)
        .setMessage("Add file2\nnew line\nanother line").call();
    RebaseResult result = git.rebase().setOperation(Operation.CONTINUE)
        .call();
    assertEquals(Status.OK, result.getStatus());

  }

  @Test
  public void testRebaseShouldStopForEditInCaseOfConflict()
      throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    //change file1
    writeTrashFile(FILE1, FILE1 + "a");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Change file1").call();

    //change file1
    writeTrashFile(FILE1, FILE1 + "b");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Change file1").call();

    RebaseResult result = git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            steps.remove(0);
            try {
              steps.get(0).setAction(Action.EDIT);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return commit;
          }
        }).call();
    assertEquals(Status.STOPPED, result.getStatus());
    git.add().addFilepattern(FILE1).call();
    result = git.rebase().setOperation(Operation.CONTINUE).call();
    assertEquals(Status.EDIT, result.getStatus());

  }

  @Test
  public void testRebaseShouldStopForRewordInCaseOfConflict()
      throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // change file1
    writeTrashFile(FILE1, FILE1 + "a");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Change file1").call();

    // change file1
    writeTrashFile(FILE1, FILE1 + "b");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Change file1").call();

    RebaseResult result = git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            steps.remove(0);
            try {
              steps.get(0).setAction(Action.REWORD);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return "rewritten commit message";
          }
        }).call();
    assertEquals(Status.STOPPED, result.getStatus());
    git.add().addFilepattern(FILE1).call();
    result = git.rebase().runInteractively(new InteractiveHandler() {

      public void prepareSteps(List<RebaseTodoLine> steps) {
        steps.remove(0);
        try {
          steps.get(0).setAction(Action.REWORD);
        } catch (IllegalTodoFileModification e) {
          fail("unexpected exception: " + e);
        }
      }

      public String modifyCommitMessage(String commit) {
        return "rewritten commit message";
      }
    }).setOperation(Operation.CONTINUE).call();
    assertEquals(Status.OK, result.getStatus());
    Iterator<RevCommit> logIterator = git.log().all().call().iterator();
    String actualCommitMag = logIterator.next().getShortMessage();
    assertEquals("rewritten commit message", actualCommitMag);

  }

  @Test
  public void testRebaseShouldSquashInCaseOfConflict() throws Exception {
    // create file1 on master
    writeTrashFile(FILE1, FILE1);
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Add file1\nnew line").call();
    assertTrue(new File(db.getWorkTree(), FILE1).exists());

    // change file2
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Change file2").call();

    // change file1
    writeTrashFile(FILE1, FILE1 + "a");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Change file1").call();

    // change file1
    writeTrashFile(FILE1, FILE1 + "b");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Change file1").call();

    RebaseResult result = git.rebase().setUpstream("HEAD~3")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.PICK);
              steps.remove(1);
              steps.get(1).setAction(Action.SQUASH);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return "squashed message";
          }
        }).call();
    assertEquals(Status.STOPPED, result.getStatus());
    git.add().addFilepattern(FILE1).call();
    result = git.rebase().runInteractively(new InteractiveHandler() {

      public void prepareSteps(List<RebaseTodoLine> steps) {
        try {
          steps.get(0).setAction(Action.PICK);
          steps.remove(1);
          steps.get(1).setAction(Action.SQUASH);
        } catch (IllegalTodoFileModification e) {
          fail("unexpected exception: " + e);
        }
      }

      public String modifyCommitMessage(String commit) {
        return "squashed message";
      }
    }).setOperation(Operation.CONTINUE).call();
    assertEquals(Status.OK, result.getStatus());
    Iterator<RevCommit> logIterator = git.log().all().call().iterator();
    String actualCommitMag = logIterator.next().getShortMessage();
    assertEquals("squashed message", actualCommitMag);
  }

  @Test
  public void testRebaseShouldFixupInCaseOfConflict() 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());

    // change file2
    writeTrashFile("file2", "file2");
    git.add().addFilepattern("file2").call();
    git.commit().setMessage("Change file2").call();

    // change file1
    writeTrashFile(FILE1, FILE1 + "a");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("Change file1").call();

    // change file1, add file3
    writeTrashFile(FILE1, FILE1 + "b");
    writeTrashFile("file3", "file3");
    git.add().addFilepattern(FILE1).call();
    git.add().addFilepattern("file3").call();
    git.commit().setMessage("Change file1, add file3").call();

    RebaseResult result = git.rebase().setUpstream("HEAD~3")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.PICK);
              steps.remove(1);
              steps.get(1).setAction(Action.FIXUP);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return commit;
          }
        }).call();
    assertEquals(Status.STOPPED, result.getStatus());
    git.add().addFilepattern(FILE1).call();
    result = git.rebase().runInteractively(new InteractiveHandler() {

      public void prepareSteps(List<RebaseTodoLine> steps) {
        try {
          steps.get(0).setAction(Action.PICK);
          steps.remove(1);
          steps.get(1).setAction(Action.FIXUP);
        } catch (IllegalTodoFileModification e) {
          fail("unexpected exception: " + e);
        }
      }

      public String modifyCommitMessage(String commit) {
        return "commit";
      }
    }).setOperation(Operation.CONTINUE).call();
    assertEquals(Status.OK, result.getStatus());
    Iterator<RevCommit> logIterator = git.log().all().call().iterator();
    String actualCommitMsg = logIterator.next().getShortMessage();
    assertEquals("Change file2", actualCommitMsg);
    actualCommitMsg = logIterator.next().getShortMessage();
    assertEquals("Add file1", actualCommitMsg);
    assertTrue(new File(db.getWorkTree(), "file3").exists());

  }

  @Test
  public void testInteractiveRebaseWithModificationShouldNotDeleteDataOnAbort()
      throws Exception {
    // create file0 + file1, add and commit
    writeTrashFile("file0", "file0");
    writeTrashFile(FILE1, "file1");
    git.add().addFilepattern("file0").addFilepattern(FILE1).call();
    git.commit().setMessage("commit1").call();

    // modify file1, add and commit
    writeTrashFile(FILE1, "modified file1");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("commit2").call();

    // modify file1, add and commit
    writeTrashFile(FILE1, "modified file1 a second time");
    git.add().addFilepattern(FILE1).call();
    git.commit().setMessage("commit3").call();

    // modify file0, but do not commit
    writeTrashFile("file0", "modified file0 in index");
    git.add().addFilepattern("file0").addFilepattern(FILE1).call();
    // do not commit
    writeTrashFile("file0", "modified file0");

    // start rebase
    RebaseResult result = git.rebase().setUpstream("HEAD~2")
        .runInteractively(new InteractiveHandler() {

          public void prepareSteps(List<RebaseTodoLine> steps) {
            try {
              steps.get(0).setAction(Action.EDIT);
              steps.get(1).setAction(Action.PICK);
            } catch (IllegalTodoFileModification e) {
              fail("unexpected exception: " + e);
            }
          }

          public String modifyCommitMessage(String commit) {
            return commit;
          }
        }).call();
    // the following condition was true before commit 83b6ab233:
    // jgit started the rebase and deleted the change on abort
    // This test should verify that content was deleted
    if (result.getStatus() == Status.EDIT)
      git.rebase().setOperation(Operation.ABORT).call();

    checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
    checkFile(new File(db.getWorkTree(), "file1"),
        "modified file1 a second time");
    assertEquals("[file0, mode:100644, content:modified file0 in index]"
        + "[file1, mode:100644, content:modified file1 a second time]",
        indexState(CONTENT));

  }

  private File getTodoFile() {
    File todoFile = new File(db.getDirectory(), GIT_REBASE_TODO);
    return todoFile;
  }
}
TOP

Related Classes of org.eclipse.jgit.api.RebaseCommandTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.