View Javadoc
1   /*
2    * Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler@sap.com>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.api;
44  
45  import static org.hamcrest.CoreMatchers.equalTo;
46  import static org.hamcrest.CoreMatchers.not;
47  import static org.hamcrest.MatcherAssert.assertThat;
48  import static org.junit.Assert.assertEquals;
49  import static org.junit.Assert.assertFalse;
50  import static org.junit.Assert.assertNotNull;
51  import static org.junit.Assert.assertTrue;
52  import static org.junit.Assert.fail;
53  
54  import java.io.BufferedReader;
55  import java.io.File;
56  import java.io.FileInputStream;
57  import java.io.IOException;
58  import java.io.InputStreamReader;
59  import java.util.Collections;
60  import java.util.Iterator;
61  import java.util.List;
62  
63  import org.eclipse.jgit.api.MergeResult.MergeStatus;
64  import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
65  import org.eclipse.jgit.api.RebaseCommand.Operation;
66  import org.eclipse.jgit.api.RebaseResult.Status;
67  import org.eclipse.jgit.api.errors.InvalidRebaseStepException;
68  import org.eclipse.jgit.api.errors.RefNotFoundException;
69  import org.eclipse.jgit.api.errors.UnmergedPathsException;
70  import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
71  import org.eclipse.jgit.diff.DiffEntry;
72  import org.eclipse.jgit.dircache.DirCacheCheckout;
73  import org.eclipse.jgit.errors.AmbiguousObjectException;
74  import org.eclipse.jgit.errors.IllegalTodoFileModification;
75  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
76  import org.eclipse.jgit.errors.MissingObjectException;
77  import org.eclipse.jgit.junit.RepositoryTestCase;
78  import org.eclipse.jgit.lib.AbbreviatedObjectId;
79  import org.eclipse.jgit.lib.ConfigConstants;
80  import org.eclipse.jgit.lib.Constants;
81  import org.eclipse.jgit.lib.ObjectId;
82  import org.eclipse.jgit.lib.ObjectLoader;
83  import org.eclipse.jgit.lib.PersonIdent;
84  import org.eclipse.jgit.lib.RebaseTodoLine;
85  import org.eclipse.jgit.lib.RebaseTodoLine.Action;
86  import org.eclipse.jgit.lib.RefUpdate;
87  import org.eclipse.jgit.lib.ReflogEntry;
88  import org.eclipse.jgit.lib.RepositoryState;
89  import org.eclipse.jgit.merge.MergeStrategy;
90  import org.eclipse.jgit.revwalk.RevCommit;
91  import org.eclipse.jgit.revwalk.RevSort;
92  import org.eclipse.jgit.revwalk.RevWalk;
93  import org.eclipse.jgit.treewalk.TreeWalk;
94  import org.eclipse.jgit.treewalk.filter.TreeFilter;
95  import org.eclipse.jgit.util.FileUtils;
96  import org.eclipse.jgit.util.IO;
97  import org.eclipse.jgit.util.RawParseUtils;
98  import org.junit.Before;
99  import org.junit.Test;
100 
101 public class RebaseCommandTest extends RepositoryTestCase {
102 	private static final String GIT_REBASE_TODO = "rebase-merge/git-rebase-todo";
103 
104 	private static final String FILE1 = "file1";
105 
106 	protected Git git;
107 
108 	@Override
109 	@Before
110 	public void setUp() throws Exception {
111 		super.setUp();
112 		this.git = new Git(db);
113 	}
114 
115 	private void checkoutCommit(RevCommit commit) throws IllegalStateException,
116 			IOException {
117 		RevCommit head;
118 		try (RevWalk walk = new RevWalk(db)) {
119 			head = walk.parseCommit(db.resolve(Constants.HEAD));
120 			DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(),
121 					db.lockDirCache(), commit.getTree());
122 			dco.setFailOnConflict(true);
123 			dco.checkout();
124 		}
125 		// update the HEAD
126 		RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
127 		refUpdate.setNewObjectId(commit);
128 		refUpdate.setRefLogMessage("checkout: moving to " + head.getName(),
129 				false);
130 		refUpdate.forceUpdate();
131 	}
132 
133 	@Test
134 	public void testFastForwardWithNewFile() throws Exception {
135 		// create file1 on master
136 		writeTrashFile(FILE1, FILE1);
137 		git.add().addFilepattern(FILE1).call();
138 		RevCommit first = git.commit().setMessage("Add file1").call();
139 
140 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
141 		// create a topic branch
142 		createBranch(first, "refs/heads/topic");
143 		// create file2 on master
144 		File file2 = writeTrashFile("file2", "file2");
145 		git.add().addFilepattern("file2").call();
146 		RevCommit second = git.commit().setMessage("Add file2").call();
147 		assertTrue(new File(db.getWorkTree(), "file2").exists());
148 
149 		checkoutBranch("refs/heads/topic");
150 		assertFalse(new File(db.getWorkTree(), "file2").exists());
151 
152 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
153 		assertTrue(new File(db.getWorkTree(), "file2").exists());
154 		checkFile(file2, "file2");
155 		assertEquals(Status.FAST_FORWARD, res.getStatus());
156 
157 		List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
158 				.getReverseEntries();
159 		List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
160 				.getReverseEntries();
161 		List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
162 				.getReverseEntries();
163 		assertEquals("rebase finished: returning to refs/heads/topic", headLog
164 				.get(0).getComment());
165 		assertEquals("checkout: moving from topic to " + second.getName(),
166 				headLog.get(1).getComment());
167 		assertEquals(2, masterLog.size());
168 		assertEquals(2, topicLog.size());
169 		assertEquals(
170 				"rebase finished: refs/heads/topic onto " + second.getName(),
171 				topicLog.get(0).getComment());
172 	}
173 
174 	@Test
175 	public void testFastForwardWithMultipleCommits() throws Exception {
176 		// create file1 on master
177 		writeTrashFile(FILE1, FILE1);
178 		git.add().addFilepattern(FILE1).call();
179 		RevCommit first = git.commit().setMessage("Add file1").call();
180 
181 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
182 		// create a topic branch
183 		createBranch(first, "refs/heads/topic");
184 		// create file2 on master
185 		File file2 = writeTrashFile("file2", "file2");
186 		git.add().addFilepattern("file2").call();
187 		git.commit().setMessage("Add file2").call();
188 		assertTrue(new File(db.getWorkTree(), "file2").exists());
189 		// write a second commit
190 		writeTrashFile("file2", "file2 new content");
191 		git.add().addFilepattern("file2").call();
192 		RevCommit second = git.commit().setMessage("Change content of file2")
193 				.call();
194 
195 		checkoutBranch("refs/heads/topic");
196 		assertFalse(new File(db.getWorkTree(), "file2").exists());
197 
198 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
199 		assertTrue(new File(db.getWorkTree(), "file2").exists());
200 		checkFile(file2, "file2 new content");
201 		assertEquals(Status.FAST_FORWARD, res.getStatus());
202 
203 		List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
204 				.getReverseEntries();
205 		List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
206 				.getReverseEntries();
207 		List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
208 				.getReverseEntries();
209 		assertEquals("rebase finished: returning to refs/heads/topic", headLog
210 				.get(0).getComment());
211 		assertEquals("checkout: moving from topic to " + second.getName(),
212 				headLog.get(1).getComment());
213 		assertEquals(3, masterLog.size());
214 		assertEquals(2, topicLog.size());
215 		assertEquals(
216 				"rebase finished: refs/heads/topic onto " + second.getName(),
217 				topicLog.get(0).getComment());
218 	}
219 
220 	/**
221 	 * Create the following commits and then attempt to rebase topic onto
222 	 * master. This will serialize the branches.
223 	 *
224 	 * <pre>
225 	 * A - B (master)
226 	 *   \
227 	 *    C - D - F (topic)
228 	 *     \      /
229 	 *      E  -  (side)
230 	 * </pre>
231 	 *
232 	 * into
233 	 *
234 	 * <pre>
235 	 * A - B - (master)  C' - D' - E' (topic')
236 	 *   \
237 	 *    C - D - F (topic)
238 	 *     \      /
239 	 *      E  -  (side)
240 	 * </pre>
241 	 *
242 	 * @throws Exception
243 	 */
244 	@Test
245 	public void testRebaseShouldIgnoreMergeCommits()
246 			throws Exception {
247 		// create file1 on master
248 		writeTrashFile(FILE1, FILE1);
249 		git.add().addFilepattern(FILE1).call();
250 		RevCommit a = git.commit().setMessage("Add file1").call();
251 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
252 
253 		// create a topic branch
254 		createBranch(a, "refs/heads/topic");
255 
256 		// update FILE1 on master
257 		writeTrashFile(FILE1, "blah");
258 		git.add().addFilepattern(FILE1).call();
259 		RevCommit b = git.commit().setMessage("updated file1 on master").call();
260 
261 		checkoutBranch("refs/heads/topic");
262 		writeTrashFile("file3", "more changess");
263 		git.add().addFilepattern("file3").call();
264 		RevCommit c = git.commit()
265 				.setMessage("update file3 on topic").call();
266 
267 		// create a branch from the topic commit
268 		createBranch(c, "refs/heads/side");
269 
270 		// second commit on topic
271 		writeTrashFile("file2", "file2");
272 		git.add().addFilepattern("file2").call();
273 		RevCommit d = git.commit().setMessage("Add file2").call();
274 		assertTrue(new File(db.getWorkTree(), "file2").exists());
275 
276 		// switch to side branch and update file2
277 		checkoutBranch("refs/heads/side");
278 		writeTrashFile("file3", "more change");
279 		git.add().addFilepattern("file3").call();
280 		RevCommit e = git.commit().setMessage("update file2 on side")
281 				.call();
282 
283 		// switch back to topic and merge in side, creating f
284 		checkoutBranch("refs/heads/topic");
285 		MergeResult result = git.merge().include(e.getId())
286 				.setStrategy(MergeStrategy.RESOLVE).call();
287 		assertEquals(MergeStatus.MERGED, result.getMergeStatus());
288 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
289 		assertEquals(Status.OK, res.getStatus());
290 
291 		try (RevWalk rw = new RevWalk(db)) {
292 			rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic")));
293 			assertDerivedFrom(rw.next(), e);
294 			assertDerivedFrom(rw.next(), d);
295 			assertDerivedFrom(rw.next(), c);
296 			assertEquals(b, rw.next());
297 			assertEquals(a, rw.next());
298 		}
299 
300 		List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
301 				.getReverseEntries();
302 		List<ReflogEntry> sideLog = db.getReflogReader("refs/heads/side")
303 				.getReverseEntries();
304 		List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
305 				.getReverseEntries();
306 		List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
307 				.getReverseEntries();
308 		assertEquals("rebase finished: returning to refs/heads/topic", headLog
309 				.get(0).getComment());
310 		assertEquals("rebase: update file2 on side", headLog.get(1)
311 				.getComment());
312 		assertEquals("rebase: Add file2", headLog.get(2).getComment());
313 		assertEquals("rebase: update file3 on topic", headLog.get(3)
314 				.getComment());
315 		assertEquals("checkout: moving from topic to " + b.getName(), headLog
316 				.get(4).getComment());
317 		assertEquals(2, masterLog.size());
318 		assertEquals(2, sideLog.size());
319 		assertEquals(5, topicLog.size());
320 		assertEquals("rebase finished: refs/heads/topic onto " + b.getName(),
321 				topicLog.get(0).getComment());
322 	}
323 
324 	static void assertDerivedFrom(RevCommit derived, RevCommit original) {
325 		assertThat(derived, not(equalTo(original)));
326 		assertEquals(original.getFullMessage(), derived.getFullMessage());
327 	}
328 
329 	@Test
330 	public void testRebasePreservingMerges1() throws Exception {
331 		doTestRebasePreservingMerges(true);
332 	}
333 
334 	@Test
335 	public void testRebasePreservingMerges2() throws Exception {
336 		doTestRebasePreservingMerges(false);
337 	}
338 
339 	/**
340 	 * Transforms the same before-state as in
341 	 * {@link #testRebaseShouldIgnoreMergeCommits()} to the following.
342 	 * <p>
343 	 * This test should always rewrite E.
344 	 *
345 	 * <pre>
346 	 * A - B (master) - - -  C' - D' - F' (topic')
347 	 *   \                    \       /
348 	 *    C - D - F (topic)      - E'
349 	 *     \     /
350 	 *       - E (side)
351 	 * </pre>
352 	 *
353 	 * @param testConflict
354 	 * @throws Exception
355 	 */
356 	private void doTestRebasePreservingMerges(boolean testConflict)
357 			throws Exception {
358 		// create file1 on master
359 		writeTrashFile(FILE1, FILE1);
360 		git.add().addFilepattern(FILE1).call();
361 		RevCommit a = git.commit().setMessage("commit a").call();
362 
363 		// create a topic branch
364 		createBranch(a, "refs/heads/topic");
365 
366 		// update FILE1 on master
367 		writeTrashFile(FILE1, "blah");
368 		writeTrashFile("conflict", "b");
369 		git.add().addFilepattern(".").call();
370 		RevCommit b = git.commit().setMessage("commit b").call();
371 
372 		checkoutBranch("refs/heads/topic");
373 		writeTrashFile("file3", "more changess");
374 		git.add().addFilepattern("file3").call();
375 		RevCommit c = git.commit().setMessage("commit c").call();
376 
377 		// create a branch from the topic commit
378 		createBranch(c, "refs/heads/side");
379 
380 		// second commit on topic
381 		writeTrashFile("file2", "file2");
382 		if (testConflict)
383 			writeTrashFile("conflict", "d");
384 		git.add().addFilepattern(".").call();
385 		RevCommit d = git.commit().setMessage("commit d").call();
386 		assertTrue(new File(db.getWorkTree(), "file2").exists());
387 
388 		// switch to side branch and update file2
389 		checkoutBranch("refs/heads/side");
390 		writeTrashFile("file3", "more change");
391 		if (testConflict)
392 			writeTrashFile("conflict", "e");
393 		git.add().addFilepattern(".").call();
394 		RevCommit e = git.commit().setMessage("commit e").call();
395 
396 		// switch back to topic and merge in side, creating f
397 		checkoutBranch("refs/heads/topic");
398 		MergeResult result = git.merge().include(e.getId())
399 				.setStrategy(MergeStrategy.RESOLVE).call();
400 		final RevCommit f;
401 		if (testConflict) {
402 			assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
403 			assertEquals(Collections.singleton("conflict"), git.status().call()
404 					.getConflicting());
405 			// resolve
406 			writeTrashFile("conflict", "f resolved");
407 			git.add().addFilepattern("conflict").call();
408 			f = git.commit().setMessage("commit f").call();
409 		} else {
410 			assertEquals(MergeStatus.MERGED, result.getMergeStatus());
411 			try (RevWalk rw = new RevWalk(db)) {
412 				f = rw.parseCommit(result.getNewHead());
413 			}
414 		}
415 
416 		RebaseResult res = git.rebase().setUpstream("refs/heads/master")
417 				.setPreserveMerges(true).call();
418 		if (testConflict) {
419 			// first there is a conflict whhen applying d
420 			assertEquals(Status.STOPPED, res.getStatus());
421 			assertEquals(Collections.singleton("conflict"), git.status().call()
422 					.getConflicting());
423 			assertTrue(read("conflict").contains("\nb\n=======\nd\n"));
424 			// resolve
425 			writeTrashFile("conflict", "d new");
426 			git.add().addFilepattern("conflict").call();
427 			res = git.rebase().setOperation(Operation.CONTINUE).call();
428 
429 			// then there is a conflict when applying e
430 			assertEquals(Status.STOPPED, res.getStatus());
431 			assertEquals(Collections.singleton("conflict"), git.status().call()
432 					.getConflicting());
433 			assertTrue(read("conflict").contains("\nb\n=======\ne\n"));
434 			// resolve
435 			writeTrashFile("conflict", "e new");
436 			git.add().addFilepattern("conflict").call();
437 			res = git.rebase().setOperation(Operation.CONTINUE).call();
438 
439 			// finally there is a conflict merging e'
440 			assertEquals(Status.STOPPED, res.getStatus());
441 			assertEquals(Collections.singleton("conflict"), git.status().call()
442 					.getConflicting());
443 			assertTrue(read("conflict").contains("\nd new\n=======\ne new\n"));
444 			// resolve
445 			writeTrashFile("conflict", "f new resolved");
446 			git.add().addFilepattern("conflict").call();
447 			res = git.rebase().setOperation(Operation.CONTINUE).call();
448 		}
449 		assertEquals(Status.OK, res.getStatus());
450 
451 		if (testConflict)
452 			assertEquals("f new resolved", read("conflict"));
453 		assertEquals("blah", read(FILE1));
454 		assertEquals("file2", read("file2"));
455 		assertEquals("more change", read("file3"));
456 
457 		try (RevWalk rw = new RevWalk(db)) {
458 			rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic")));
459 			RevCommit newF = rw.next();
460 			assertDerivedFrom(newF, f);
461 			assertEquals(2, newF.getParentCount());
462 			RevCommit newD = rw.next();
463 			assertDerivedFrom(newD, d);
464 			if (testConflict)
465 				assertEquals("d new", readFile("conflict", newD));
466 			RevCommit newE = rw.next();
467 			assertDerivedFrom(newE, e);
468 			if (testConflict)
469 				assertEquals("e new", readFile("conflict", newE));
470 			assertEquals(newD, newF.getParent(0));
471 			assertEquals(newE, newF.getParent(1));
472 			assertDerivedFrom(rw.next(), c);
473 			assertEquals(b, rw.next());
474 			assertEquals(a, rw.next());
475 		}
476 	}
477 
478 	private String readFile(String path, RevCommit commit) throws IOException {
479 		try (TreeWalk walk = TreeWalk.forPath(db, path, commit.getTree())) {
480 			ObjectLoader loader = db.open(walk.getObjectId(0),
481 					Constants.OBJ_BLOB);
482 			String result = RawParseUtils.decode(loader.getCachedBytes());
483 			return result;
484 		}
485 	}
486 
487 	@Test
488 	public void testRebasePreservingMergesWithUnrelatedSide1() throws Exception {
489 		doTestRebasePreservingMergesWithUnrelatedSide(true);
490 	}
491 
492 	@Test
493 	public void testRebasePreservingMergesWithUnrelatedSide2() throws Exception {
494 		doTestRebasePreservingMergesWithUnrelatedSide(false);
495 	}
496 
497 	/**
498 	 * Rebase topic onto master, not rewriting E. The merge resulting in D is
499 	 * confliicting to show that the manual merge resolution survives the
500 	 * rebase.
501 	 *
502 	 * <pre>
503 	 * A - B - G (master)
504 	 *  \   \
505 	 *   \   C - D - F (topic)
506 	 *    \     /
507 	 *      E (side)
508 	 * </pre>
509 	 *
510 	 * <pre>
511 	 * A - B - G (master)
512 	 *  \       \
513 	 *   \       C' - D' - F' (topic')
514 	 *    \          /
515 	 *      E (side)
516 	 * </pre>
517 	 *
518 	 * @param testConflict
519 	 * @throws Exception
520 	 */
521 	private void doTestRebasePreservingMergesWithUnrelatedSide(
522 			boolean testConflict) throws Exception {
523 		try (RevWalk rw = new RevWalk(db)) {
524 			rw.sort(RevSort.TOPO);
525 
526 			writeTrashFile(FILE1, FILE1);
527 			git.add().addFilepattern(FILE1).call();
528 			RevCommit a = git.commit().setMessage("commit a").call();
529 
530 			writeTrashFile("file2", "blah");
531 			git.add().addFilepattern("file2").call();
532 			RevCommit b = git.commit().setMessage("commit b").call();
533 
534 			// create a topic branch
535 			createBranch(b, "refs/heads/topic");
536 			checkoutBranch("refs/heads/topic");
537 
538 			writeTrashFile("file3", "more changess");
539 			writeTrashFile(FILE1, "preparing conflict");
540 			git.add().addFilepattern("file3").addFilepattern(FILE1).call();
541 			RevCommit c = git.commit().setMessage("commit c").call();
542 
543 			createBranch(a, "refs/heads/side");
544 			checkoutBranch("refs/heads/side");
545 			writeTrashFile("conflict", "e");
546 			writeTrashFile(FILE1, FILE1 + "\n" + "line 2");
547 			git.add().addFilepattern(".").call();
548 			RevCommit e = git.commit().setMessage("commit e").call();
549 
550 			// switch back to topic and merge in side, creating d
551 			checkoutBranch("refs/heads/topic");
552 			MergeResult result = git.merge().include(e)
553 					.setStrategy(MergeStrategy.RESOLVE).call();
554 
555 			assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
556 			assertEquals(result.getConflicts().keySet(),
557 					Collections.singleton(FILE1));
558 			writeTrashFile(FILE1, "merge resolution");
559 			git.add().addFilepattern(FILE1).call();
560 			RevCommit d = git.commit().setMessage("commit d").call();
561 
562 			RevCommit f = commitFile("file2", "new content two", "topic");
563 
564 			checkoutBranch("refs/heads/master");
565 			writeTrashFile("fileg", "fileg");
566 			if (testConflict)
567 				writeTrashFile("conflict", "g");
568 			git.add().addFilepattern(".").call();
569 			RevCommit g = git.commit().setMessage("commit g").call();
570 
571 			checkoutBranch("refs/heads/topic");
572 			RebaseResult res = git.rebase().setUpstream("refs/heads/master")
573 					.setPreserveMerges(true).call();
574 			if (testConflict) {
575 				assertEquals(Status.STOPPED, res.getStatus());
576 				assertEquals(Collections.singleton("conflict"), git.status().call()
577 						.getConflicting());
578 				// resolve
579 				writeTrashFile("conflict", "e");
580 				git.add().addFilepattern("conflict").call();
581 				res = git.rebase().setOperation(Operation.CONTINUE).call();
582 			}
583 			assertEquals(Status.OK, res.getStatus());
584 
585 			assertEquals("merge resolution", read(FILE1));
586 			assertEquals("new content two", read("file2"));
587 			assertEquals("more changess", read("file3"));
588 			assertEquals("fileg", read("fileg"));
589 
590 			rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic")));
591 			RevCommit newF = rw.next();
592 			assertDerivedFrom(newF, f);
593 			RevCommit newD = rw.next();
594 			assertDerivedFrom(newD, d);
595 			assertEquals(2, newD.getParentCount());
596 			RevCommit newC = rw.next();
597 			assertDerivedFrom(newC, c);
598 			RevCommit newE = rw.next();
599 			assertEquals(e, newE);
600 			assertEquals(newC, newD.getParent(0));
601 			assertEquals(e, newD.getParent(1));
602 			assertEquals(g, rw.next());
603 			assertEquals(b, rw.next());
604 			assertEquals(a, rw.next());
605 		}
606 	}
607 
608 	@Test
609 	public void testRebaseParentOntoHeadShouldBeUptoDate() throws Exception {
610 		writeTrashFile(FILE1, FILE1);
611 		git.add().addFilepattern(FILE1).call();
612 		RevCommit parent = git.commit().setMessage("parent comment").call();
613 
614 		writeTrashFile(FILE1, "another change");
615 		git.add().addFilepattern(FILE1).call();
616 		git.commit().setMessage("head commit").call();
617 
618 		RebaseResult result = git.rebase().setUpstream(parent).call();
619 		assertEquals(Status.UP_TO_DATE, result.getStatus());
620 
621 		assertEquals(2, db.getReflogReader(Constants.HEAD).getReverseEntries()
622 				.size());
623 		assertEquals(2, db.getReflogReader("refs/heads/master")
624 				.getReverseEntries().size());
625 	}
626 
627 	@Test
628 	public void testUpToDate() throws Exception {
629 		// create file1 on master
630 		writeTrashFile(FILE1, FILE1);
631 		git.add().addFilepattern(FILE1).call();
632 		RevCommit first = git.commit().setMessage("Add file1").call();
633 
634 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
635 
636 		RebaseResult res = git.rebase().setUpstream(first).call();
637 		assertEquals(Status.UP_TO_DATE, res.getStatus());
638 
639 		assertEquals(1, db.getReflogReader(Constants.HEAD).getReverseEntries()
640 				.size());
641 		assertEquals(1, db.getReflogReader("refs/heads/master")
642 				.getReverseEntries().size());
643 	}
644 
645 	@Test
646 	public void testUnknownUpstream() throws Exception {
647 		// create file1 on master
648 		writeTrashFile(FILE1, FILE1);
649 		git.add().addFilepattern(FILE1).call();
650 		git.commit().setMessage("Add file1").call();
651 
652 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
653 
654 		try {
655 			git.rebase().setUpstream("refs/heads/xyz").call();
656 			fail("expected exception was not thrown");
657 		} catch (RefNotFoundException e) {
658 			// expected exception
659 		}
660 	}
661 
662 	@Test
663 	public void testConflictFreeWithSingleFile() throws Exception {
664 		// create file1 on master
665 		File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
666 		git.add().addFilepattern(FILE1).call();
667 		RevCommit second = git.commit().setMessage("Add file1").call();
668 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
669 		// change first line in master and commit
670 		writeTrashFile(FILE1, "1master\n2\n3\n");
671 		checkFile(theFile, "1master\n2\n3\n");
672 		git.add().addFilepattern(FILE1).call();
673 		RevCommit lastMasterChange = git.commit().setMessage(
674 				"change file1 in master").call();
675 
676 		// create a topic branch based on second commit
677 		createBranch(second, "refs/heads/topic");
678 		checkoutBranch("refs/heads/topic");
679 		// we have the old content again
680 		checkFile(theFile, "1\n2\n3\n");
681 
682 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
683 		// change third line in topic branch
684 		writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
685 		git.add().addFilepattern(FILE1).call();
686 		RevCommit origHead = git.commit().setMessage("change file1 in topic")
687 				.call();
688 
689 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
690 		assertEquals(Status.OK, res.getStatus());
691 		checkFile(theFile, "1master\n2\n3\ntopic\n");
692 		// our old branch should be checked out again
693 		assertEquals("refs/heads/topic", db.getFullBranch());
694 		try (RevWalk rw = new RevWalk(db)) {
695 			assertEquals(lastMasterChange, rw.parseCommit(
696 					db.resolve(Constants.HEAD)).getParent(0));
697 		}
698 		assertEquals(origHead, db.readOrigHead());
699 		List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
700 				.getReverseEntries();
701 		List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
702 				.getReverseEntries();
703 		List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
704 				.getReverseEntries();
705 		assertEquals(2, masterLog.size());
706 		assertEquals(3, topicLog.size());
707 		assertEquals("rebase finished: refs/heads/topic onto "
708 				+ lastMasterChange.getName(), topicLog.get(0).getComment());
709 		assertEquals("rebase finished: returning to refs/heads/topic", headLog
710 				.get(0).getComment());
711 	}
712 
713 	@Test
714 	public void testDetachedHead() throws Exception {
715 		// create file1 on master
716 		File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
717 		git.add().addFilepattern(FILE1).call();
718 		RevCommit second = git.commit().setMessage("Add file1").call();
719 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
720 		// change first line in master and commit
721 		writeTrashFile(FILE1, "1master\n2\n3\n");
722 		checkFile(theFile, "1master\n2\n3\n");
723 		git.add().addFilepattern(FILE1).call();
724 		RevCommit lastMasterChange = git.commit().setMessage(
725 				"change file1 in master").call();
726 
727 		// create a topic branch based on second commit
728 		createBranch(second, "refs/heads/topic");
729 		checkoutBranch("refs/heads/topic");
730 		// we have the old content again
731 		checkFile(theFile, "1\n2\n3\n");
732 
733 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
734 		// change third line in topic branch
735 		writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
736 		git.add().addFilepattern(FILE1).call();
737 		RevCommit topicCommit = git.commit()
738 				.setMessage("change file1 in topic").call();
739 		checkoutBranch("refs/heads/master");
740 		checkoutCommit(topicCommit);
741 		assertEquals(topicCommit.getId().getName(), db.getFullBranch());
742 
743 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
744 		assertEquals(Status.OK, res.getStatus());
745 		checkFile(theFile, "1master\n2\n3\ntopic\n");
746 		try (RevWalk rw = new RevWalk(db)) {
747 			assertEquals(lastMasterChange, rw.parseCommit(
748 					db.resolve(Constants.HEAD)).getParent(0));
749 		}
750 
751 		List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
752 				.getReverseEntries();
753 		assertEquals(8, headLog.size());
754 		assertEquals("rebase: change file1 in topic", headLog.get(0)
755 				.getComment());
756 		assertEquals("checkout: moving from " + topicCommit.getName() + " to "
757 				+ lastMasterChange.getName(), headLog.get(1).getComment());
758 	}
759 
760 	@Test
761 	public void testFilesAddedFromTwoBranches() throws Exception {
762 		// create file1 on master
763 		writeTrashFile(FILE1, FILE1);
764 		git.add().addFilepattern(FILE1).call();
765 		RevCommit masterCommit = git.commit().setMessage("Add file1 to master")
766 				.call();
767 
768 		// create a branch named file2 and add file2
769 		createBranch(masterCommit, "refs/heads/file2");
770 		checkoutBranch("refs/heads/file2");
771 		writeTrashFile("file2", "file2");
772 		git.add().addFilepattern("file2").call();
773 		RevCommit addFile2 = git.commit().setMessage(
774 				"Add file2 to branch file2").call();
775 
776 		// create a branch named file3 and add file3
777 		createBranch(masterCommit, "refs/heads/file3");
778 		checkoutBranch("refs/heads/file3");
779 		writeTrashFile("file3", "file3");
780 		git.add().addFilepattern("file3").call();
781 		git.commit().setMessage("Add file3 to branch file3").call();
782 
783 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
784 		assertFalse(new File(db.getWorkTree(), "file2").exists());
785 		assertTrue(new File(db.getWorkTree(), "file3").exists());
786 
787 		RebaseResult res = git.rebase().setUpstream("refs/heads/file2").call();
788 		assertEquals(Status.OK, res.getStatus());
789 
790 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
791 		assertTrue(new File(db.getWorkTree(), "file2").exists());
792 		assertTrue(new File(db.getWorkTree(), "file3").exists());
793 
794 		// our old branch should be checked out again
795 		assertEquals("refs/heads/file3", db.getFullBranch());
796 		try (RevWalk rw = new RevWalk(db)) {
797 			assertEquals(addFile2, rw.parseCommit(
798 					db.resolve(Constants.HEAD)).getParent(0));
799 		}
800 
801 		checkoutBranch("refs/heads/file2");
802 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
803 		assertTrue(new File(db.getWorkTree(), "file2").exists());
804 		assertFalse(new File(db.getWorkTree(), "file3").exists());
805 	}
806 
807 	@Test
808 	public void testStopOnConflict() throws Exception {
809 		// create file1 on master
810 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
811 				"2", "3");
812 		// change first line in master
813 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
814 		checkFile(FILE1, "1master", "2", "3");
815 		// create a topic branch based on second commit
816 		createBranch(firstInMaster, "refs/heads/topic");
817 		checkoutBranch("refs/heads/topic");
818 		// we have the old content again
819 		checkFile(FILE1, "1", "2", "3");
820 
821 		// add a line (non-conflicting)
822 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
823 				"3", "topic4");
824 
825 		// change first line (conflicting)
826 		RevCommit conflicting = writeFileAndCommit(FILE1,
827 				"change file1 in topic", "1topic", "2", "3", "topic4");
828 
829 		RevCommit lastTopicCommit = writeFileAndCommit(FILE1,
830 				"change file1 in topic again", "1topic", "2", "3", "topic4");
831 
832 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
833 		assertEquals(Status.STOPPED, res.getStatus());
834 		assertEquals(conflicting, res.getCurrentCommit());
835 		checkFile(FILE1,
836 				"<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
837 				">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4");
838 
839 		assertEquals(RepositoryState.REBASING_MERGE, db
840 				.getRepositoryState());
841 		assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
842 		// the first one should be included, so we should have left two picks in
843 		// the file
844 		assertEquals(1, countPicks());
845 
846 		// rebase should not succeed in this state
847 		try {
848 			git.rebase().setUpstream("refs/heads/master").call();
849 			fail("Expected exception was not thrown");
850 		} catch (WrongRepositoryStateException e) {
851 			// expected
852 		}
853 
854 		// abort should reset to topic branch
855 		res = git.rebase().setOperation(Operation.ABORT).call();
856 		assertEquals(res.getStatus(), Status.ABORTED);
857 		assertEquals("refs/heads/topic", db.getFullBranch());
858 		checkFile(FILE1, "1topic", "2", "3", "topic4");
859 		try (RevWalk rw = new RevWalk(db)) {
860 			assertEquals(lastTopicCommit,
861 					rw.parseCommit(db.resolve(Constants.HEAD)));
862 		}
863 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
864 
865 		// rebase- dir in .git must be deleted
866 		assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
867 	}
868 
869 	@Test
870 	public void testStopOnConflictAndAbortWithDetachedHEAD() throws Exception {
871 		// create file1 on master
872 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
873 				"2", "3");
874 		// change first line in master
875 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
876 		checkFile(FILE1, "1master", "2", "3");
877 		// create a topic branch based on second commit
878 		createBranch(firstInMaster, "refs/heads/topic");
879 		checkoutBranch("refs/heads/topic");
880 		// we have the old content again
881 		checkFile(FILE1, "1", "2", "3");
882 
883 		// add a line (non-conflicting)
884 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
885 				"3", "topic4");
886 
887 		// change first line (conflicting)
888 		RevCommit conflicting = writeFileAndCommit(FILE1,
889 				"change file1 in topic", "1topic", "2", "3", "topic4");
890 
891 		RevCommit lastTopicCommit = writeFileAndCommit(FILE1,
892 				"change file1 in topic again", "1topic", "2", "3", "topic4");
893 
894 		git.checkout().setName(lastTopicCommit.getName()).call();
895 
896 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
897 		assertEquals(Status.STOPPED, res.getStatus());
898 		assertEquals(conflicting, res.getCurrentCommit());
899 		checkFile(FILE1,
900 				"<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
901 				">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4");
902 
903 		assertEquals(RepositoryState.REBASING_MERGE,
904 				db.getRepositoryState());
905 		assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
906 		// the first one should be included, so we should have left two picks in
907 		// the file
908 		assertEquals(1, countPicks());
909 
910 		// rebase should not succeed in this state
911 		try {
912 			git.rebase().setUpstream("refs/heads/master").call();
913 			fail("Expected exception was not thrown");
914 		} catch (WrongRepositoryStateException e) {
915 			// expected
916 		}
917 
918 		// abort should reset to topic branch
919 		res = git.rebase().setOperation(Operation.ABORT).call();
920 		assertEquals(res.getStatus(), Status.ABORTED);
921 		assertEquals(lastTopicCommit.getName(), db.getFullBranch());
922 		checkFile(FILE1, "1topic", "2", "3", "topic4");
923 		try (RevWalk rw = new RevWalk(db)) {
924 			assertEquals(lastTopicCommit,
925 					rw.parseCommit(db.resolve(Constants.HEAD)));
926 		}
927 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
928 
929 		// rebase- dir in .git must be deleted
930 		assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
931 	}
932 
933 	@Test
934 	public void testStopOnConflictAndContinue() throws Exception {
935 		// create file1 on master
936 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
937 				"2", "3");
938 		// change in master
939 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
940 
941 		checkFile(FILE1, "1master", "2", "3");
942 		// create a topic branch based on the first commit
943 		createBranch(firstInMaster, "refs/heads/topic");
944 		checkoutBranch("refs/heads/topic");
945 		// we have the old content again
946 		checkFile(FILE1, "1", "2", "3");
947 
948 		// add a line (non-conflicting)
949 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
950 				"3", "4topic");
951 
952 		// change first line (conflicting)
953 		writeFileAndCommit(FILE1,
954 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
955 				"3", "4topic");
956 
957 		// change second line (not conflicting)
958 		writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
959 				"2topic", "3", "4topic");
960 
961 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
962 		assertEquals(Status.STOPPED, res.getStatus());
963 
964 		// continue should throw a meaningful exception
965 		try {
966 			res = git.rebase().setOperation(Operation.CONTINUE).call();
967 			fail("Expected Exception not thrown");
968 		} catch (UnmergedPathsException e) {
969 			// expected
970 		}
971 
972 		// merge the file; the second topic commit should go through
973 		writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
974 
975 		res = git.rebase().setOperation(Operation.CONTINUE).call();
976 		assertNotNull(res);
977 		assertEquals(Status.OK, res.getStatus());
978 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
979 
980 		ObjectId headId = db.resolve(Constants.HEAD);
981 		try (RevWalk rw = new RevWalk(db)) {
982 			RevCommit rc = rw.parseCommit(headId);
983 			RevCommit parent = rw.parseCommit(rc.getParent(0));
984 			assertEquals("change file1 in topic\n\nThis is conflicting", parent
985 					.getFullMessage());
986 		}
987 	}
988 
989 	@Test
990 	public void testStopOnConflictAndContinueWithNoDeltaToMaster()
991 			throws Exception {
992 		// create file1 on master
993 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
994 				"2", "3");
995 		// change in master
996 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
997 
998 		checkFile(FILE1, "1master", "2", "3");
999 		// create a topic branch based on the first commit
1000 		createBranch(firstInMaster, "refs/heads/topic");
1001 		checkoutBranch("refs/heads/topic");
1002 		// we have the old content again
1003 		checkFile(FILE1, "1", "2", "3");
1004 
1005 		// change first line (conflicting)
1006 		writeFileAndCommit(FILE1,
1007 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
1008 				"3", "4topic");
1009 
1010 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1011 		assertEquals(Status.STOPPED, res.getStatus());
1012 
1013 		// continue should throw a meaningful exception
1014 		try {
1015 			res = git.rebase().setOperation(Operation.CONTINUE).call();
1016 			fail("Expected Exception not thrown");
1017 		} catch (UnmergedPathsException e) {
1018 			// expected
1019 		}
1020 
1021 		// merge the file; the second topic commit should go through
1022 		writeFileAndAdd(FILE1, "1master", "2", "3");
1023 
1024 		res = git.rebase().setOperation(Operation.CONTINUE).call();
1025 		assertNotNull(res);
1026 		assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
1027 		assertEquals(RepositoryState.REBASING_MERGE,
1028 				db.getRepositoryState());
1029 
1030 		git.rebase().setOperation(Operation.SKIP).call();
1031 
1032 		ObjectId headId = db.resolve(Constants.HEAD);
1033 		try (RevWalk rw = new RevWalk(db)) {
1034 			RevCommit rc = rw.parseCommit(headId);
1035 			assertEquals("change file1 in master", rc.getFullMessage());
1036 		}
1037 	}
1038 
1039 	@Test
1040 	public void testStopOnConflictAndFailContinueIfFileIsDirty()
1041 			throws Exception {
1042 		// create file1 on master
1043 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
1044 				"2", "3");
1045 		// change in master
1046 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
1047 
1048 		checkFile(FILE1, "1master", "2", "3");
1049 		// create a topic branch based on the first commit
1050 		createBranch(firstInMaster, "refs/heads/topic");
1051 		checkoutBranch("refs/heads/topic");
1052 		// we have the old content again
1053 		checkFile(FILE1, "1", "2", "3");
1054 
1055 		// add a line (non-conflicting)
1056 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
1057 				"3", "4topic");
1058 
1059 		// change first line (conflicting)
1060 		writeFileAndCommit(FILE1,
1061 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
1062 				"3", "4topic");
1063 
1064 		// change second line (not conflicting)
1065 		writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
1066 				"2topic", "3", "4topic");
1067 
1068 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1069 		assertEquals(Status.STOPPED, res.getStatus());
1070 
1071 		git.add().addFilepattern(FILE1).call();
1072 		File trashFile = writeTrashFile(FILE1, "Some local change");
1073 
1074 		res = git.rebase().setOperation(Operation.CONTINUE).call();
1075 		assertNotNull(res);
1076 		assertEquals(Status.STOPPED, res.getStatus());
1077 		checkFile(trashFile, "Some local change");
1078 	}
1079 
1080 	@Test
1081 	public void testStopOnLastConflictAndContinue() throws Exception {
1082 		// create file1 on master
1083 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
1084 				"2", "3");
1085 		// change in master
1086 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
1087 
1088 		checkFile(FILE1, "1master", "2", "3");
1089 		// create a topic branch based on the first commit
1090 		createBranch(firstInMaster, "refs/heads/topic");
1091 		checkoutBranch("refs/heads/topic");
1092 		// we have the old content again
1093 		checkFile(FILE1, "1", "2", "3");
1094 
1095 		// add a line (non-conflicting)
1096 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
1097 				"3", "4topic");
1098 
1099 		// change first line (conflicting)
1100 		writeFileAndCommit(FILE1,
1101 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
1102 				"3", "4topic");
1103 
1104 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1105 		assertEquals(Status.STOPPED, res.getStatus());
1106 
1107 		// merge the file; the second topic commit should go through
1108 		writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
1109 
1110 		res = git.rebase().setOperation(Operation.CONTINUE).call();
1111 		assertNotNull(res);
1112 		assertEquals(Status.OK, res.getStatus());
1113 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1114 	}
1115 
1116 	@Test
1117 	public void testStopOnLastConflictAndSkip() throws Exception {
1118 		// create file1 on master
1119 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
1120 				"2", "3");
1121 		// change in master
1122 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
1123 
1124 		checkFile(FILE1, "1master", "2", "3");
1125 		// create a topic branch based on the first commit
1126 		createBranch(firstInMaster, "refs/heads/topic");
1127 		checkoutBranch("refs/heads/topic");
1128 		// we have the old content again
1129 		checkFile(FILE1, "1", "2", "3");
1130 
1131 		// add a line (non-conflicting)
1132 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
1133 				"3", "4topic");
1134 
1135 		// change first line (conflicting)
1136 		writeFileAndCommit(FILE1,
1137 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
1138 				"3", "4topic");
1139 
1140 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1141 		assertEquals(Status.STOPPED, res.getStatus());
1142 
1143 		// merge the file; the second topic commit should go through
1144 		writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
1145 
1146 		res = git.rebase().setOperation(Operation.SKIP).call();
1147 		assertNotNull(res);
1148 		assertEquals(Status.OK, res.getStatus());
1149 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1150 	}
1151 
1152 	@Test
1153 	public void testMergeFirstStopOnLastConflictAndSkip() throws Exception {
1154 		// create file1 on master
1155 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
1156 				"2", "3");
1157 		// change in master
1158 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
1159 
1160 		checkFile(FILE1, "1master", "2", "3");
1161 		// create a topic branch based on the first commit
1162 		createBranch(firstInMaster, "refs/heads/topic");
1163 		checkoutBranch("refs/heads/topic");
1164 		// we have the old content again
1165 		checkFile(FILE1, "1", "2", "3");
1166 
1167 		// add a line (conflicting)
1168 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1topic",
1169 				"2", "3", "4topic");
1170 
1171 		// change first line (conflicting again)
1172 		writeFileAndCommit(FILE1,
1173 				"change file1 in topic\n\nThis is conflicting", "1topicagain",
1174 				"2", "3", "4topic");
1175 
1176 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1177 		assertEquals(Status.STOPPED, res.getStatus());
1178 
1179 		writeFileAndAdd(FILE1, "merged");
1180 
1181 		res = git.rebase().setOperation(Operation.CONTINUE).call();
1182 		assertEquals(Status.STOPPED, res.getStatus());
1183 
1184 		res = git.rebase().setOperation(Operation.SKIP).call();
1185 		assertNotNull(res);
1186 		assertEquals(Status.OK, res.getStatus());
1187 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1188 		checkFile(FILE1, "merged");
1189 	}
1190 
1191 	@Test
1192 	public void testStopOnConflictAndSkipNoConflict() throws Exception {
1193 		// create file1 on master
1194 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
1195 				"2", "3");
1196 		// change in master
1197 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
1198 
1199 		checkFile(FILE1, "1master", "2", "3");
1200 		// create a topic branch based on the first commit
1201 		createBranch(firstInMaster, "refs/heads/topic");
1202 		checkoutBranch("refs/heads/topic");
1203 		// we have the old content again
1204 		checkFile(FILE1, "1", "2", "3");
1205 
1206 		// add a line (non-conflicting)
1207 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
1208 				"3", "4topic");
1209 
1210 		// change first line (conflicting)
1211 		writeFileAndCommit(FILE1,
1212 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
1213 				"3", "4topic");
1214 
1215 		// change third line (not conflicting)
1216 		writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
1217 				"3topic", "4topic");
1218 
1219 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1220 		assertEquals(Status.STOPPED, res.getStatus());
1221 
1222 		res = git.rebase().setOperation(Operation.SKIP).call();
1223 
1224 		checkFile(FILE1, "1master", "2", "3topic", "4topic");
1225 		assertEquals(Status.OK, res.getStatus());
1226 	}
1227 
1228 	@Test
1229 	public void testStopOnConflictAndSkipWithConflict() throws Exception {
1230 		// create file1 on master
1231 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
1232 				"2", "3", "4");
1233 		// change in master
1234 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2",
1235 				"3master", "4");
1236 
1237 		checkFile(FILE1, "1master", "2", "3master", "4");
1238 		// create a topic branch based on the first commit
1239 		createBranch(firstInMaster, "refs/heads/topic");
1240 		checkoutBranch("refs/heads/topic");
1241 		// we have the old content again
1242 		checkFile(FILE1, "1", "2", "3", "4");
1243 
1244 		// add a line (non-conflicting)
1245 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
1246 				"3", "4", "5topic");
1247 
1248 		// change first line (conflicting)
1249 		writeFileAndCommit(FILE1,
1250 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
1251 				"3", "4", "5topic");
1252 
1253 		// change third line (conflicting)
1254 		writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
1255 				"3topic", "4", "5topic");
1256 
1257 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1258 		assertEquals(Status.STOPPED, res.getStatus());
1259 
1260 		res = git.rebase().setOperation(Operation.SKIP).call();
1261 		// TODO is this correct? It is what the command line returns
1262 		checkFile(
1263 				FILE1,
1264 				"1master\n2\n<<<<<<< Upstream, based on master\n3master\n=======\n3topic",
1265 				">>>>>>> 5afc8df change file1 in topic again\n4\n5topic");
1266 		assertEquals(Status.STOPPED, res.getStatus());
1267 	}
1268 
1269 	@Test
1270 	public void testStopOnConflictCommitAndContinue() throws Exception {
1271 		// create file1 on master
1272 		RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
1273 				"2", "3");
1274 		// change in master
1275 		writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
1276 
1277 		checkFile(FILE1, "1master", "2", "3");
1278 		// create a topic branch based on the first commit
1279 		createBranch(firstInMaster, "refs/heads/topic");
1280 		checkoutBranch("refs/heads/topic");
1281 		// we have the old content again
1282 		checkFile(FILE1, "1", "2", "3");
1283 
1284 		// add a line (non-conflicting)
1285 		writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
1286 				"3", "4topic");
1287 
1288 		// change first line (conflicting)
1289 		writeFileAndCommit(FILE1,
1290 				"change file1 in topic\n\nThis is conflicting", "1topic", "2",
1291 				"3", "4topic");
1292 
1293 		// change second line (not conflicting)
1294 		writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
1295 				"3topic", "4topic");
1296 
1297 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1298 		assertEquals(Status.STOPPED, res.getStatus());
1299 
1300 		// continue should throw a meaningful exception
1301 		try {
1302 			res = git.rebase().setOperation(Operation.CONTINUE).call();
1303 			fail("Expected Exception not thrown");
1304 		} catch (UnmergedPathsException e) {
1305 			// expected
1306 		}
1307 
1308 		// merge the file; the second topic commit should go through
1309 		writeFileAndCommit(FILE1, "A different commit message", "1topic", "2",
1310 				"3", "4topic");
1311 
1312 		res = git.rebase().setOperation(Operation.CONTINUE).call();
1313 		assertNotNull(res);
1314 
1315 		// nothing to commit. this leaves the repo state in rebase, so that the
1316 		// user can decide what to do. if he accidentally committed, reset soft,
1317 		// and continue, if he really has nothing to commit, skip.
1318 		assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
1319 		assertEquals(RepositoryState.REBASING_MERGE,
1320 				db.getRepositoryState());
1321 
1322 		git.rebase().setOperation(Operation.SKIP).call();
1323 
1324 		ObjectId headId = db.resolve(Constants.HEAD);
1325 		try (RevWalk rw = new RevWalk(db)) {
1326 			RevCommit rc = rw.parseCommit(headId);
1327 			RevCommit parent = rw.parseCommit(rc.getParent(0));
1328 			assertEquals("A different commit message", parent.getFullMessage());
1329 		}
1330 	}
1331 
1332 	private RevCommit writeFileAndCommit(String fileName, String commitMessage,
1333 			String... lines) throws Exception {
1334 		StringBuilder sb = new StringBuilder();
1335 		for (String line : lines) {
1336 			sb.append(line);
1337 			sb.append('\n');
1338 		}
1339 		writeTrashFile(fileName, sb.toString());
1340 		git.add().addFilepattern(fileName).call();
1341 		return git.commit().setMessage(commitMessage).call();
1342 	}
1343 
1344 	private void writeFileAndAdd(String fileName, String... lines)
1345 			throws Exception {
1346 		StringBuilder sb = new StringBuilder();
1347 		for (String line : lines) {
1348 			sb.append(line);
1349 			sb.append('\n');
1350 		}
1351 		writeTrashFile(fileName, sb.toString());
1352 		git.add().addFilepattern(fileName).call();
1353 	}
1354 
1355 	private void checkFile(String fileName, String... lines) throws Exception {
1356 		File file = new File(db.getWorkTree(), fileName);
1357 		StringBuilder sb = new StringBuilder();
1358 		for (String line : lines) {
1359 			sb.append(line);
1360 			sb.append('\n');
1361 		}
1362 		checkFile(file, sb.toString());
1363 	}
1364 
1365 	@Test
1366 	public void testStopOnConflictFileCreationAndDeletion() throws Exception {
1367 		// create file1 on master
1368 		writeTrashFile(FILE1, "Hello World");
1369 		git.add().addFilepattern(FILE1).call();
1370 		// create file2 on master
1371 		File file2 = writeTrashFile("file2", "Hello World 2");
1372 		git.add().addFilepattern("file2").call();
1373 		// create file3 on master
1374 		File file3 = writeTrashFile("file3", "Hello World 3");
1375 		git.add().addFilepattern("file3").call();
1376 
1377 		RevCommit firstInMaster = git.commit()
1378 				.setMessage("Add file 1, 2 and 3").call();
1379 
1380 		// create file4 on master
1381 		File file4 = writeTrashFile("file4", "Hello World 4");
1382 		git.add().addFilepattern("file4").call();
1383 
1384 		deleteTrashFile("file2");
1385 		git.add().setUpdate(true).addFilepattern("file2").call();
1386 		// create folder folder6 on topic (conflicts with file folder6 on topic
1387 		// later on)
1388 		writeTrashFile("folder6/file1", "Hello World folder6");
1389 		git.add().addFilepattern("folder6/file1").call();
1390 
1391 		git.commit().setMessage(
1392 				"Add file 4 and folder folder6, delete file2 on master").call();
1393 
1394 		// create a topic branch based on second commit
1395 		createBranch(firstInMaster, "refs/heads/topic");
1396 		checkoutBranch("refs/heads/topic");
1397 
1398 		deleteTrashFile("file3");
1399 		git.add().setUpdate(true).addFilepattern("file3").call();
1400 		// create file5 on topic
1401 		File file5 = writeTrashFile("file5", "Hello World 5");
1402 		git.add().addFilepattern("file5").call();
1403 		git.commit().setMessage("Delete file3 and add file5 in topic").call();
1404 
1405 		// create file folder6 on topic (conflicts with folder6 on master)
1406 		writeTrashFile("folder6", "Hello World 6");
1407 		git.add().addFilepattern("folder6").call();
1408 		// create file7 on topic
1409 		File file7 = writeTrashFile("file7", "Hello World 7");
1410 		git.add().addFilepattern("file7").call();
1411 
1412 		deleteTrashFile("file5");
1413 		git.add().setUpdate(true).addFilepattern("file5").call();
1414 		RevCommit conflicting = git.commit().setMessage(
1415 				"Delete file5, add file folder6 and file7 in topic").call();
1416 
1417 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
1418 		assertEquals(Status.STOPPED, res.getStatus());
1419 		assertEquals(conflicting, res.getCurrentCommit());
1420 
1421 		assertEquals(RepositoryState.REBASING_MERGE, db
1422 				.getRepositoryState());
1423 		assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
1424 		// the first one should be included, so we should have left two picks in
1425 		// the file
1426 		assertEquals(0, countPicks());
1427 
1428 		assertFalse(file2.exists());
1429 		assertFalse(file3.exists());
1430 		assertTrue(file4.exists());
1431 		assertFalse(file5.exists());
1432 		assertTrue(file7.exists());
1433 
1434 		// abort should reset to topic branch
1435 		res = git.rebase().setOperation(Operation.ABORT).call();
1436 		assertEquals(res.getStatus(), Status.ABORTED);
1437 		assertEquals("refs/heads/topic", db.getFullBranch());
1438 		try (RevWalk rw = new RevWalk(db)) {
1439 			assertEquals(conflicting, rw.parseCommit(db.resolve(Constants.HEAD)));
1440 			assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1441 		}
1442 
1443 		// rebase- dir in .git must be deleted
1444 		assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
1445 
1446 		assertTrue(file2.exists());
1447 		assertFalse(file3.exists());
1448 		assertFalse(file4.exists());
1449 		assertFalse(file5.exists());
1450 		assertTrue(file7.exists());
1451 
1452 	}
1453 
1454 	@Test
1455 	public void testAuthorScriptConverter() throws Exception {
1456 		// -1 h timezone offset
1457 		PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com",
1458 				123456789123L, -60);
1459 		String convertedAuthor = git.rebase().toAuthorScript(ident);
1460 		String[] lines = convertedAuthor.split("\n");
1461 		assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
1462 		assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
1463 		assertEquals("GIT_AUTHOR_DATE='@123456789 -0100'", lines[2]);
1464 
1465 		PersonIdent parsedIdent = git.rebase().parseAuthor(
1466 				convertedAuthor.getBytes("UTF-8"));
1467 		assertEquals(ident.getName(), parsedIdent.getName());
1468 		assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
1469 		// this is rounded to the last second
1470 		assertEquals(123456789000L, parsedIdent.getWhen().getTime());
1471 		assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
1472 
1473 		// + 9.5h timezone offset
1474 		ident = new PersonIdent("Author name", "a.mail@some.com",
1475 				123456789123L, +570);
1476 		convertedAuthor = git.rebase().toAuthorScript(ident);
1477 		lines = convertedAuthor.split("\n");
1478 		assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
1479 		assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
1480 		assertEquals("GIT_AUTHOR_DATE='@123456789 +0930'", lines[2]);
1481 
1482 		parsedIdent = git.rebase().parseAuthor(
1483 				convertedAuthor.getBytes("UTF-8"));
1484 		assertEquals(ident.getName(), parsedIdent.getName());
1485 		assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
1486 		assertEquals(123456789000L, parsedIdent.getWhen().getTime());
1487 		assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
1488 	}
1489 
1490 	@Test
1491 	public void testRepositoryStateChecks() throws Exception {
1492 		try {
1493 			git.rebase().setOperation(Operation.ABORT).call();
1494 			fail("Expected Exception not thrown");
1495 		} catch (WrongRepositoryStateException e) {
1496 			// expected
1497 		}
1498 		try {
1499 			git.rebase().setOperation(Operation.SKIP).call();
1500 			fail("Expected Exception not thrown");
1501 		} catch (WrongRepositoryStateException e) {
1502 			// expected
1503 		}
1504 		try {
1505 			git.rebase().setOperation(Operation.CONTINUE).call();
1506 			fail("Expected Exception not thrown");
1507 		} catch (WrongRepositoryStateException e) {
1508 			// expected
1509 		}
1510 	}
1511 
1512 	@Test
1513 	public void testRebaseWithUntrackedFile() throws Exception {
1514 		// create file1, add and commit
1515 		writeTrashFile(FILE1, "file1");
1516 		git.add().addFilepattern(FILE1).call();
1517 		RevCommit commit = git.commit().setMessage("commit1").call();
1518 
1519 		// create topic branch and checkout / create file2, add and commit
1520 		createBranch(commit, "refs/heads/topic");
1521 		checkoutBranch("refs/heads/topic");
1522 		writeTrashFile("file2", "file2");
1523 		git.add().addFilepattern("file2").call();
1524 		git.commit().setMessage("commit2").call();
1525 
1526 		// checkout master branch / modify file1, add and commit
1527 		checkoutBranch("refs/heads/master");
1528 		writeTrashFile(FILE1, "modified file1");
1529 		git.add().addFilepattern(FILE1).call();
1530 		git.commit().setMessage("commit3").call();
1531 
1532 		// checkout topic branch / create untracked file3
1533 		checkoutBranch("refs/heads/topic");
1534 		writeTrashFile("file3", "untracked file3");
1535 
1536 		// rebase
1537 		assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master")
1538 				.call().getStatus());
1539 	}
1540 
1541 	@Test
1542 	public void testRebaseWithUnstagedTopicChange() throws Exception {
1543 		// create file1, add and commit
1544 		writeTrashFile(FILE1, "file1");
1545 		git.add().addFilepattern(FILE1).call();
1546 		RevCommit commit = git.commit().setMessage("commit1").call();
1547 
1548 		// create topic branch and checkout / create file2, add and commit
1549 		createBranch(commit, "refs/heads/topic");
1550 		checkoutBranch("refs/heads/topic");
1551 		writeTrashFile("file2", "file2");
1552 		git.add().addFilepattern("file2").call();
1553 		git.commit().setMessage("commit2").call();
1554 
1555 		// checkout master branch / modify file1, add and commit
1556 		checkoutBranch("refs/heads/master");
1557 		writeTrashFile(FILE1, "modified file1");
1558 		git.add().addFilepattern(FILE1).call();
1559 		git.commit().setMessage("commit3").call();
1560 
1561 		// checkout topic branch / modify file2
1562 		checkoutBranch("refs/heads/topic");
1563 		writeTrashFile("file2", "unstaged file2");
1564 
1565 		// rebase
1566 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1567 				.call();
1568 		assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
1569 		assertEquals(1, result.getUncommittedChanges().size());
1570 		assertEquals("file2", result.getUncommittedChanges().get(0));
1571 	}
1572 
1573 	@Test
1574 	public void testRebaseWithUncommittedTopicChange() throws Exception {
1575 		// create file1, add and commit
1576 		writeTrashFile(FILE1, "file1");
1577 		git.add().addFilepattern(FILE1).call();
1578 		RevCommit commit = git.commit().setMessage("commit1").call();
1579 
1580 		// create topic branch and checkout / create file2, add and commit
1581 		createBranch(commit, "refs/heads/topic");
1582 		checkoutBranch("refs/heads/topic");
1583 		writeTrashFile("file2", "file2");
1584 		git.add().addFilepattern("file2").call();
1585 		git.commit().setMessage("commit2").call();
1586 
1587 		// checkout master branch / modify file1, add and commit
1588 		checkoutBranch("refs/heads/master");
1589 		writeTrashFile(FILE1, "modified file1");
1590 		git.add().addFilepattern(FILE1).call();
1591 		git.commit().setMessage("commit3").call();
1592 
1593 		// checkout topic branch / modify file2 and add
1594 		checkoutBranch("refs/heads/topic");
1595 		File uncommittedFile = writeTrashFile("file2", "uncommitted file2");
1596 		git.add().addFilepattern("file2").call();
1597 		// do not commit
1598 
1599 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1600 				.call();
1601 		assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
1602 		assertEquals(1, result.getUncommittedChanges().size());
1603 		assertEquals("file2", result.getUncommittedChanges().get(0));
1604 
1605 		checkFile(uncommittedFile, "uncommitted file2");
1606 		assertEquals(RepositoryState.SAFE, git.getRepository().getRepositoryState());
1607 	}
1608 
1609 	@Test
1610 	public void testRebaseWithUnstagedMasterChange() throws Exception {
1611 		// create file1, add and commit
1612 		writeTrashFile(FILE1, "file1");
1613 		git.add().addFilepattern(FILE1).call();
1614 		RevCommit commit = git.commit().setMessage("commit1").call();
1615 
1616 		// create topic branch and checkout / create file2, add and commit
1617 		createBranch(commit, "refs/heads/topic");
1618 		checkoutBranch("refs/heads/topic");
1619 		writeTrashFile("file2", "file2");
1620 		git.add().addFilepattern("file2").call();
1621 		git.commit().setMessage("commit2").call();
1622 
1623 		// checkout master branch / modify file1, add and commit
1624 		checkoutBranch("refs/heads/master");
1625 		writeTrashFile(FILE1, "modified file1");
1626 		git.add().addFilepattern(FILE1).call();
1627 		git.commit().setMessage("commit3").call();
1628 
1629 		// checkout topic branch / modify file1
1630 		checkoutBranch("refs/heads/topic");
1631 		writeTrashFile(FILE1, "unstaged modified file1");
1632 
1633 		// rebase
1634 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1635 				.call();
1636 		assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
1637 		assertEquals(1, result.getUncommittedChanges().size());
1638 		assertEquals(FILE1, result.getUncommittedChanges().get(0));
1639 	}
1640 
1641 	@Test
1642 	public void testRebaseWithUncommittedMasterChange() throws Exception {
1643 		// create file1, add and commit
1644 		writeTrashFile(FILE1, "file1");
1645 		git.add().addFilepattern(FILE1).call();
1646 		RevCommit commit = git.commit().setMessage("commit1").call();
1647 
1648 		// create topic branch and checkout / create file2, add and commit
1649 		createBranch(commit, "refs/heads/topic");
1650 		checkoutBranch("refs/heads/topic");
1651 		writeTrashFile("file2", "file2");
1652 		git.add().addFilepattern("file2").call();
1653 		git.commit().setMessage("commit2").call();
1654 
1655 		// checkout master branch / modify file1, add and commit
1656 		checkoutBranch("refs/heads/master");
1657 		writeTrashFile(FILE1, "modified file1");
1658 		git.add().addFilepattern(FILE1).call();
1659 		git.commit().setMessage("commit3").call();
1660 
1661 		// checkout topic branch / modify file1 and add
1662 		checkoutBranch("refs/heads/topic");
1663 		writeTrashFile(FILE1, "uncommitted modified file1");
1664 		git.add().addFilepattern(FILE1).call();
1665 		// do not commit
1666 
1667 		// rebase
1668 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1669 				.call();
1670 		assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
1671 		assertEquals(1, result.getUncommittedChanges().size());
1672 		assertEquals(FILE1, result.getUncommittedChanges().get(0));
1673 	}
1674 
1675 	@Test
1676 	public void testRebaseWithUnstagedMasterChangeBaseCommit() throws Exception {
1677 		// create file0 + file1, add and commit
1678 		writeTrashFile("file0", "file0");
1679 		writeTrashFile(FILE1, "file1");
1680 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
1681 		RevCommit commit = git.commit().setMessage("commit1").call();
1682 
1683 		// create topic branch and checkout / create file2, add and commit
1684 		createBranch(commit, "refs/heads/topic");
1685 		checkoutBranch("refs/heads/topic");
1686 		writeTrashFile("file2", "file2");
1687 		git.add().addFilepattern("file2").call();
1688 		git.commit().setMessage("commit2").call();
1689 
1690 		// checkout master branch / modify file1, add and commit
1691 		checkoutBranch("refs/heads/master");
1692 		writeTrashFile(FILE1, "modified file1");
1693 		git.add().addFilepattern(FILE1).call();
1694 		git.commit().setMessage("commit3").call();
1695 
1696 		// checkout topic branch / modify file0
1697 		checkoutBranch("refs/heads/topic");
1698 		writeTrashFile("file0", "unstaged modified file0");
1699 
1700 		// rebase
1701 		assertEquals(Status.UNCOMMITTED_CHANGES,
1702 				git.rebase().setUpstream("refs/heads/master")
1703 				.call().getStatus());
1704 	}
1705 
1706 	@Test
1707 	public void testRebaseWithUncommittedMasterChangeBaseCommit()
1708 			throws Exception {
1709 		// create file0 + file1, add and commit
1710 		File file0 = writeTrashFile("file0", "file0");
1711 		writeTrashFile(FILE1, "file1");
1712 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
1713 		RevCommit commit = git.commit().setMessage("commit1").call();
1714 
1715 		// create topic branch and checkout / create file2, add and commit
1716 		createBranch(commit, "refs/heads/topic");
1717 		checkoutBranch("refs/heads/topic");
1718 		writeTrashFile("file2", "file2");
1719 		git.add().addFilepattern("file2").call();
1720 		git.commit().setMessage("commit2").call();
1721 
1722 		// checkout master branch / modify file1, add and commit
1723 		checkoutBranch("refs/heads/master");
1724 		writeTrashFile(FILE1, "modified file1");
1725 		git.add().addFilepattern(FILE1).call();
1726 		git.commit().setMessage("commit3").call();
1727 
1728 		// checkout topic branch / modify file0 and add
1729 		checkoutBranch("refs/heads/topic");
1730 		write(file0, "unstaged modified file0");
1731 		git.add().addFilepattern("file0").call();
1732 		// do not commit
1733 
1734 		// get current index state
1735 		String indexState = indexState(CONTENT);
1736 
1737 		// rebase
1738 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1739 				.call();
1740 		assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
1741 		assertEquals(1, result.getUncommittedChanges().size());
1742 		// index shall be unchanged
1743 		assertEquals(indexState, indexState(CONTENT));
1744 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1745 	}
1746 
1747 	@Test
1748 	public void testRebaseWithUnstagedMasterChangeOtherCommit()
1749 			throws Exception {
1750 		// create file0, add and commit
1751 		writeTrashFile("file0", "file0");
1752 		git.add().addFilepattern("file0").call();
1753 		git.commit().setMessage("commit0").call();
1754 		// create file1, add and commit
1755 		writeTrashFile(FILE1, "file1");
1756 		git.add().addFilepattern(FILE1).call();
1757 		RevCommit commit = git.commit().setMessage("commit1").call();
1758 
1759 		// create topic branch and checkout / create file2, add and commit
1760 		createBranch(commit, "refs/heads/topic");
1761 		checkoutBranch("refs/heads/topic");
1762 		writeTrashFile("file2", "file2");
1763 		git.add().addFilepattern("file2").call();
1764 		git.commit().setMessage("commit2").call();
1765 
1766 		// checkout master branch / modify file1, add and commit
1767 		checkoutBranch("refs/heads/master");
1768 		writeTrashFile(FILE1, "modified file1");
1769 		git.add().addFilepattern(FILE1).call();
1770 		git.commit().setMessage("commit3").call();
1771 
1772 		// checkout topic branch / modify file0
1773 		checkoutBranch("refs/heads/topic");
1774 		writeTrashFile("file0", "unstaged modified file0");
1775 
1776 		// rebase
1777 		assertEquals(Status.UNCOMMITTED_CHANGES,
1778 				git.rebase().setUpstream("refs/heads/master")
1779 				.call().getStatus());
1780 	}
1781 
1782 	@Test
1783 	public void testRebaseWithUncommittedMasterChangeOtherCommit()
1784 			throws Exception {
1785 		// create file0, add and commit
1786 		File file0 = writeTrashFile("file0", "file0");
1787 		git.add().addFilepattern("file0").call();
1788 		git.commit().setMessage("commit0").call();
1789 		// create file1, add and commit
1790 		writeTrashFile(FILE1, "file1");
1791 		git.add().addFilepattern(FILE1).call();
1792 		RevCommit commit = git.commit().setMessage("commit1").call();
1793 
1794 		// create topic branch and checkout / create file2, add and commit
1795 		createBranch(commit, "refs/heads/topic");
1796 		checkoutBranch("refs/heads/topic");
1797 		writeTrashFile("file2", "file2");
1798 		git.add().addFilepattern("file2").call();
1799 		git.commit().setMessage("commit2").call();
1800 
1801 		// checkout master branch / modify file1, add and commit
1802 		checkoutBranch("refs/heads/master");
1803 		writeTrashFile(FILE1, "modified file1");
1804 		git.add().addFilepattern(FILE1).call();
1805 		git.commit().setMessage("commit3").call();
1806 
1807 		// checkout topic branch / modify file0 and add
1808 		checkoutBranch("refs/heads/topic");
1809 		write(file0, "unstaged modified file0");
1810 		git.add().addFilepattern("file0").call();
1811 		// do not commit
1812 
1813 		// get current index state
1814 		String indexState = indexState(CONTENT);
1815 
1816 		// rebase
1817 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1818 				.call();
1819 		assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
1820 		// staged file0 causes DIRTY_INDEX
1821 		assertEquals(1, result.getUncommittedChanges().size());
1822 		assertEquals("unstaged modified file0", read(file0));
1823 		// index shall be unchanged
1824 		assertEquals(indexState, indexState(CONTENT));
1825 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1826 	}
1827 
1828 	@Test
1829 	public void testFastForwardRebaseWithModification() throws Exception {
1830 		// create file0 + file1, add and commit
1831 		writeTrashFile("file0", "file0");
1832 		writeTrashFile(FILE1, "file1");
1833 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
1834 		RevCommit commit = git.commit().setMessage("commit1").call();
1835 
1836 		// create topic branch
1837 		createBranch(commit, "refs/heads/topic");
1838 
1839 		// still on master / modify file1, add and commit
1840 		writeTrashFile(FILE1, "modified file1");
1841 		git.add().addFilepattern(FILE1).call();
1842 		git.commit().setMessage("commit2").call();
1843 
1844 		// checkout topic branch / modify file0 and add to index
1845 		checkoutBranch("refs/heads/topic");
1846 		writeTrashFile("file0", "modified file0 in index");
1847 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
1848 		// modify once more
1849 		writeTrashFile("file0", "modified file0");
1850 
1851 		// rebase
1852 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1853 				.call();
1854 		assertEquals(Status.FAST_FORWARD, result.getStatus());
1855 		checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
1856 		checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
1857 		assertEquals("[file0, mode:100644, content:modified file0 in index]"
1858 				+ "[file1, mode:100644, content:modified file1]",
1859 				indexState(CONTENT));
1860 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1861 	}
1862 
1863 	@Test
1864 	public void testRebaseWithModificationShouldNotDeleteData()
1865 			throws Exception {
1866 		// create file0 + file1, add and commit
1867 		writeTrashFile("file0", "file0");
1868 		writeTrashFile(FILE1, "file1");
1869 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
1870 		RevCommit commit = git.commit().setMessage("commit1").call();
1871 
1872 		// create topic branch
1873 		createBranch(commit, "refs/heads/topic");
1874 
1875 		// still on master / modify file1, add and commit
1876 		writeTrashFile(FILE1, "modified file1");
1877 		git.add().addFilepattern(FILE1).call();
1878 		git.commit().setMessage("commit2").call();
1879 
1880 		// checkout topic branch / modify file1, add and commit
1881 		checkoutBranch("refs/heads/topic");
1882 		writeTrashFile(FILE1, "modified file1 on topic");
1883 		git.add().addFilepattern(FILE1).call();
1884 		git.commit().setMessage("commit3").call();
1885 
1886 		writeTrashFile("file0", "modified file0");
1887 
1888 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1889 				.call();
1890 		// the following condition was true before commit 83b6ab233:
1891 		// jgit started the rebase and deleted the change on abort
1892 		// This test should verify that content was deleted
1893 		if (result.getStatus() == Status.STOPPED)
1894 			git.rebase().setOperation(Operation.ABORT).call();
1895 
1896 		checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
1897 		checkFile(new File(db.getWorkTree(), FILE1),
1898 				"modified file1 on topic");
1899 		assertEquals("[file0, mode:100644, content:file0]"
1900 				+ "[file1, mode:100644, content:modified file1 on topic]",
1901 				indexState(CONTENT));
1902 	}
1903 
1904 	@Test
1905 	public void testRebaseWithUncommittedDelete() throws Exception {
1906 		// create file0 + file1, add and commit
1907 		File file0 = writeTrashFile("file0", "file0");
1908 		writeTrashFile(FILE1, "file1");
1909 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
1910 		RevCommit commit = git.commit().setMessage("commit1").call();
1911 
1912 		// create topic branch
1913 		createBranch(commit, "refs/heads/topic");
1914 
1915 		// still on master / modify file1, add and commit
1916 		writeTrashFile(FILE1, "modified file1");
1917 		git.add().addFilepattern(FILE1).call();
1918 		git.commit().setMessage("commit2").call();
1919 
1920 		// checkout topic branch / delete file0 and add to index
1921 		checkoutBranch("refs/heads/topic");
1922 		git.rm().addFilepattern("file0").call();
1923 		// do not commit
1924 
1925 		// rebase
1926 		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
1927 				.call();
1928 		assertEquals(Status.FAST_FORWARD, result.getStatus());
1929 		assertFalse("File should still be deleted", file0.exists());
1930 		// index should only have updated file1
1931 		assertEquals("[file1, mode:100644, content:modified file1]",
1932 				indexState(CONTENT));
1933 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1934 	}
1935 
1936 	@Test
1937 	public void testRebaseWithAutoStash()
1938 			throws Exception {
1939 		// create file0, add and commit
1940 		db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
1941 				ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
1942 		writeTrashFile("file0", "file0");
1943 		git.add().addFilepattern("file0").call();
1944 		git.commit().setMessage("commit0").call();
1945 		// create file1, add and commit
1946 		writeTrashFile(FILE1, "file1");
1947 		git.add().addFilepattern(FILE1).call();
1948 		RevCommit commit = git.commit().setMessage("commit1").call();
1949 
1950 		// create topic branch and checkout / create file2, add and commit
1951 		createBranch(commit, "refs/heads/topic");
1952 		checkoutBranch("refs/heads/topic");
1953 		writeTrashFile("file2", "file2");
1954 		git.add().addFilepattern("file2").call();
1955 		git.commit().setMessage("commit2").call();
1956 
1957 		// checkout master branch / modify file1, add and commit
1958 		checkoutBranch("refs/heads/master");
1959 		writeTrashFile(FILE1, "modified file1");
1960 		git.add().addFilepattern(FILE1).call();
1961 		git.commit().setMessage("commit3").call();
1962 
1963 		// checkout topic branch / modify file0
1964 		checkoutBranch("refs/heads/topic");
1965 		writeTrashFile("file0", "unstaged modified file0");
1966 
1967 		// rebase
1968 		assertEquals(Status.OK,
1969 				git.rebase().setUpstream("refs/heads/master").call()
1970 						.getStatus());
1971 		checkFile(new File(db.getWorkTree(), "file0"),
1972 				"unstaged modified file0");
1973 		checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
1974 		checkFile(new File(db.getWorkTree(), "file2"), "file2");
1975 		assertEquals("[file0, mode:100644, content:file0]"
1976 				+ "[file1, mode:100644, content:modified file1]"
1977 				+ "[file2, mode:100644, content:file2]",
1978 				indexState(CONTENT));
1979 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
1980 	}
1981 
1982 	@Test
1983 	public void testRebaseWithAutoStashConflictOnApply() throws Exception {
1984 		// create file0, add and commit
1985 		db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
1986 				ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
1987 		writeTrashFile("file0", "file0");
1988 		git.add().addFilepattern("file0").call();
1989 		git.commit().setMessage("commit0").call();
1990 		// create file1, add and commit
1991 		writeTrashFile(FILE1, "file1");
1992 		git.add().addFilepattern(FILE1).call();
1993 		RevCommit commit = git.commit().setMessage("commit1").call();
1994 
1995 		// create topic branch and checkout / create file2, add and commit
1996 		createBranch(commit, "refs/heads/topic");
1997 		checkoutBranch("refs/heads/topic");
1998 		writeTrashFile("file2", "file2");
1999 		git.add().addFilepattern("file2").call();
2000 		git.commit().setMessage("commit2").call();
2001 
2002 		// checkout master branch / modify file1, add and commit
2003 		checkoutBranch("refs/heads/master");
2004 		writeTrashFile(FILE1, "modified file1");
2005 		git.add().addFilepattern(FILE1).call();
2006 		git.commit().setMessage("commit3").call();
2007 
2008 		// checkout topic branch / modify file0
2009 		checkoutBranch("refs/heads/topic");
2010 		writeTrashFile("file1", "unstaged modified file1");
2011 
2012 		// rebase
2013 		assertEquals(Status.STASH_APPLY_CONFLICTS,
2014 				git.rebase().setUpstream("refs/heads/master").call()
2015 						.getStatus());
2016 		checkFile(new File(db.getWorkTree(), "file0"), "file0");
2017 		checkFile(
2018 				new File(db.getWorkTree(), FILE1),
2019 				"<<<<<<< HEAD\nmodified file1\n=======\nunstaged modified file1\n>>>>>>> stash\n");
2020 		checkFile(new File(db.getWorkTree(), "file2"), "file2");
2021 		assertEquals(
2022 				"[file0, mode:100644, content:file0]"
2023 						+ "[file1, mode:100644, stage:1, content:file1]"
2024 						+ "[file1, mode:100644, stage:2, content:modified file1]"
2025 						+ "[file1, mode:100644, stage:3, content:unstaged modified file1]"
2026 						+ "[file2, mode:100644, content:file2]",
2027 				indexState(CONTENT));
2028 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
2029 
2030 		List<DiffEntry> diffs = getStashedDiff();
2031 		assertEquals(1, diffs.size());
2032 		assertEquals(DiffEntry.ChangeType.MODIFY, diffs.get(0).getChangeType());
2033 		assertEquals("file1", diffs.get(0).getOldPath());
2034 	}
2035 
2036 	@Test
2037 	public void testFastForwardRebaseWithAutoStash() throws Exception {
2038 		// create file0, add and commit
2039 		db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
2040 				ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
2041 		writeTrashFile("file0", "file0");
2042 		git.add().addFilepattern("file0").call();
2043 		git.commit().setMessage("commit0").call();
2044 		// create file1, add and commit
2045 		writeTrashFile(FILE1, "file1");
2046 		git.add().addFilepattern(FILE1).call();
2047 		RevCommit commit = git.commit().setMessage("commit1").call();
2048 
2049 		// create topic branch
2050 		createBranch(commit, "refs/heads/topic");
2051 
2052 		// checkout master branch / modify file1, add and commit
2053 		checkoutBranch("refs/heads/master");
2054 		writeTrashFile(FILE1, "modified file1");
2055 		git.add().addFilepattern(FILE1).call();
2056 		git.commit().setMessage("commit3").call();
2057 
2058 		// checkout topic branch / modify file0
2059 		checkoutBranch("refs/heads/topic");
2060 		writeTrashFile("file0", "unstaged modified file0");
2061 
2062 		// rebase
2063 		assertEquals(Status.FAST_FORWARD,
2064 				git.rebase().setUpstream("refs/heads/master")
2065 				.call().getStatus());
2066 		checkFile(new File(db.getWorkTree(), "file0"),
2067 				"unstaged modified file0");
2068 		checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
2069 		assertEquals("[file0, mode:100644, content:file0]"
2070 				+ "[file1, mode:100644, content:modified file1]",
2071 				indexState(CONTENT));
2072 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
2073 	}
2074 
2075 	private List<DiffEntry> getStashedDiff() throws AmbiguousObjectException,
2076 			IncorrectObjectTypeException, IOException, MissingObjectException {
2077 		ObjectId stashId = db.resolve("stash@{0}");
2078 		RevWalk revWalk = new RevWalk(db);
2079 		RevCommit stashCommit = revWalk.parseCommit(stashId);
2080 		List<DiffEntry> diffs = diffWorkingAgainstHead(stashCommit, revWalk);
2081 		return diffs;
2082 	}
2083 
2084 	private TreeWalk createTreeWalk() {
2085 		TreeWalk walk = new TreeWalk(db);
2086 		walk.setRecursive(true);
2087 		walk.setFilter(TreeFilter.ANY_DIFF);
2088 		return walk;
2089 	}
2090 
2091 	private List<DiffEntry> diffWorkingAgainstHead(final RevCommit commit,
2092 			RevWalk revWalk)
2093 			throws IOException {
2094 		RevCommit parentCommit = revWalk.parseCommit(commit.getParent(0));
2095 		try (TreeWalk walk = createTreeWalk()) {
2096 			walk.addTree(parentCommit.getTree());
2097 			walk.addTree(commit.getTree());
2098 			return DiffEntry.scan(walk);
2099 		}
2100 	}
2101 
2102 	private int countPicks() throws IOException {
2103 		int count = 0;
2104 		File todoFile = getTodoFile();
2105 		BufferedReader br = new BufferedReader(new InputStreamReader(
2106 				new FileInputStream(todoFile), "UTF-8"));
2107 		try {
2108 			String line = br.readLine();
2109 			while (line != null) {
2110 				int firstBlank = line.indexOf(' ');
2111 				if (firstBlank != -1) {
2112 					String actionToken = line.substring(0, firstBlank);
2113 					Action action = null;
2114 					try {
2115 						action = Action.parse(actionToken);
2116 					} catch (Exception e) {
2117 						// ignore
2118 					}
2119 					if (Action.PICK.equals(action))
2120 						count++;
2121 				}
2122 				line = br.readLine();
2123 			}
2124 			return count;
2125 		} finally {
2126 			br.close();
2127 		}
2128 	}
2129 
2130 	@Test
2131 	public void testFastForwardWithMultipleCommitsOnDifferentBranches()
2132 			throws Exception {
2133 		// create file1 on master
2134 		writeTrashFile(FILE1, FILE1);
2135 		git.add().addFilepattern(FILE1).call();
2136 		RevCommit first = git.commit().setMessage("Add file1").call();
2137 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2138 
2139 		// create a topic branch
2140 		createBranch(first, "refs/heads/topic");
2141 
2142 		// create file2 on master
2143 		writeTrashFile("file2", "file2");
2144 		git.add().addFilepattern("file2").call();
2145 		RevCommit second = git.commit().setMessage("Add file2").call();
2146 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2147 
2148 		// create side branch
2149 		createBranch(second, "refs/heads/side");
2150 
2151 		// update FILE1 on master
2152 		writeTrashFile(FILE1, "blah");
2153 		git.add().addFilepattern(FILE1).call();
2154 		git.commit().setMessage("updated file1 on master")
2155 				.call();
2156 
2157 		// switch to side branch and update file2
2158 		checkoutBranch("refs/heads/side");
2159 		writeTrashFile("file2", "more change");
2160 		git.add().addFilepattern("file2").call();
2161 		RevCommit fourth = git.commit().setMessage("update file2 on side")
2162 				.call();
2163 
2164 		// switch back to master and merge in side
2165 		checkoutBranch("refs/heads/master");
2166 		MergeResult result = git.merge().include(fourth.getId())
2167 				.setStrategy(MergeStrategy.RESOLVE).call();
2168 		assertEquals(MergeStatus.MERGED, result.getMergeStatus());
2169 
2170 		// switch back to topic branch and rebase it onto master
2171 		checkoutBranch("refs/heads/topic");
2172 		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
2173 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2174 		checkFile(new File(db.getWorkTree(), "file2"), "more change");
2175 		assertEquals(Status.FAST_FORWARD, res.getStatus());
2176 	}
2177 
2178 	@Test
2179 	public void testRebaseShouldLeaveWorkspaceUntouchedWithUnstagedChangesConflict()
2180 			throws Exception {
2181 		writeTrashFile(FILE1, "initial file");
2182 		git.add().addFilepattern(FILE1).call();
2183 		RevCommit initial = git.commit().setMessage("initial commit").call();
2184 		createBranch(initial, "refs/heads/side");
2185 
2186 		writeTrashFile(FILE1, "updated file");
2187 		git.add().addFilepattern(FILE1).call();
2188 		git.commit().setMessage("updated FILE1 on master").call();
2189 
2190 		// switch to side, modify the file
2191 		checkoutBranch("refs/heads/side");
2192 		writeTrashFile(FILE1, "side update");
2193 		git.add().addFilepattern(FILE1).call();
2194 		git.commit().setMessage("updated FILE1 on side").call();
2195 
2196 		File theFile = writeTrashFile(FILE1, "dirty the file");
2197 
2198 		// and attempt to rebase
2199 		RebaseResult rebaseResult = git.rebase()
2200 					.setUpstream("refs/heads/master").call();
2201 		assertEquals(Status.UNCOMMITTED_CHANGES, rebaseResult.getStatus());
2202 		assertEquals(1, rebaseResult.getUncommittedChanges().size());
2203 		assertEquals(FILE1, rebaseResult.getUncommittedChanges().get(0));
2204 
2205 		checkFile(theFile, "dirty the file");
2206 
2207 		assertEquals(RepositoryState.SAFE, git.getRepository()
2208 				.getRepositoryState());
2209 	}
2210 
2211 	@Test
2212 	public void testAbortShouldAlsoAbortNonInteractiveRebaseWithRebaseApplyDir()
2213 			throws Exception {
2214 		writeTrashFile(FILE1, "initial file");
2215 		git.add().addFilepattern(FILE1).call();
2216 		git.commit().setMessage("initial commit").call();
2217 
2218 		File applyDir = new File(db.getDirectory(), "rebase-apply");
2219 		File headName = new File(applyDir, "head-name");
2220 		FileUtils.mkdir(applyDir);
2221 		write(headName, "master");
2222 		db.writeOrigHead(db.resolve(Constants.HEAD));
2223 
2224 		git.rebase().setOperation(Operation.ABORT).call();
2225 
2226 		assertFalse("Abort should clean up .git/rebase-apply",
2227 				applyDir.exists());
2228 		assertEquals(RepositoryState.SAFE, git.getRepository()
2229 				.getRepositoryState());
2230 	}
2231 
2232 	@Test
2233 	public void testRebaseShouldBeAbleToHandleEmptyLinesInRebaseTodoFile()
2234 			throws IOException {
2235 		String emptyLine = "\n";
2236 		String todo = "pick 1111111 Commit 1\n" + emptyLine
2237 				+ "pick 2222222 Commit 2\n" + emptyLine
2238 				+ "# Comment line at end\n";
2239 		write(getTodoFile(), todo);
2240 
2241 		List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);
2242 		assertEquals(2, steps.size());
2243 		assertEquals("1111111", steps.get(0).getCommit().name());
2244 		assertEquals("2222222", steps.get(1).getCommit().name());
2245 	}
2246 
2247 	@Test
2248 	public void testRebaseShouldBeAbleToHandleLinesWithoutCommitMessageInRebaseTodoFile()
2249 			throws IOException {
2250 		String todo = "pick 1111111 \n" + "pick 2222222 Commit 2\n"
2251 				+ "# Comment line at end\n";
2252 		write(getTodoFile(), todo);
2253 
2254 		List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);
2255 		assertEquals(2, steps.size());
2256 		assertEquals("1111111", steps.get(0).getCommit().name());
2257 		assertEquals("2222222", steps.get(1).getCommit().name());
2258 	}
2259 
2260 	@Test
2261 	public void testRebaseShouldNotFailIfUserAddCommentLinesInPrepareSteps()
2262 			throws Exception {
2263 		commitFile(FILE1, FILE1, "master");
2264 		RevCommit c2 = commitFile("file2", "file2", "master");
2265 
2266 		// update files on master
2267 		commitFile(FILE1, "blah", "master");
2268 		RevCommit c4 = commitFile("file2", "more change", "master");
2269 
2270 		RebaseResult res = git.rebase().setUpstream("HEAD~2")
2271 				.runInteractively(new InteractiveHandler() {
2272 					@Override
2273 					public void prepareSteps(List<RebaseTodoLine> steps) {
2274 						steps.add(0, new RebaseTodoLine(
2275 								"# Comment that should not be processed"));
2276 					}
2277 
2278 					@Override
2279 					public String modifyCommitMessage(String commit) {
2280 						fail("modifyCommitMessage() was not expected to be called");
2281 						return commit;
2282 					}
2283 				}).call();
2284 
2285 		assertEquals(RebaseResult.Status.FAST_FORWARD, res.getStatus());
2286 
2287 		RebaseResult res2 = git.rebase().setUpstream("HEAD~2")
2288 				.runInteractively(new InteractiveHandler() {
2289 					@Override
2290 					public void prepareSteps(List<RebaseTodoLine> steps) {
2291 						try {
2292 							// delete RevCommit c4
2293 							steps.get(0).setAction(Action.COMMENT);
2294 						} catch (IllegalTodoFileModification e) {
2295 							fail("unexpected exception: " + e);
2296 						}
2297 					}
2298 
2299 					@Override
2300 					public String modifyCommitMessage(String commit) {
2301 						fail("modifyCommitMessage() was not expected to be called");
2302 						return commit;
2303 					}
2304 				}).call();
2305 
2306 		assertEquals(RebaseResult.Status.OK, res2.getStatus());
2307 
2308 		ObjectId headId = db.resolve(Constants.HEAD);
2309 		try (RevWalk rw = new RevWalk(db)) {
2310 			RevCommit rc = rw.parseCommit(headId);
2311 
2312 			ObjectId head1Id = db.resolve(Constants.HEAD + "~1");
2313 			RevCommit rc1 = rw.parseCommit(head1Id);
2314 
2315 			assertEquals(rc.getFullMessage(), c4.getFullMessage());
2316 			assertEquals(rc1.getFullMessage(), c2.getFullMessage());
2317 		}
2318 	}
2319 
2320 	@Test
2321 	public void testParseRewordCommand() throws Exception {
2322 		String todo = "pick 1111111 Commit 1\n"
2323 				+ "reword 2222222 Commit 2\n";
2324 		write(getTodoFile(), todo);
2325 
2326 		List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);
2327 
2328 		assertEquals(2, steps.size());
2329 		assertEquals("1111111", steps.get(0).getCommit().name());
2330 		assertEquals("2222222", steps.get(1).getCommit().name());
2331 		assertEquals(Action.REWORD, steps.get(1).getAction());
2332 	}
2333 
2334 	@Test
2335 	public void testEmptyRebaseTodo() throws Exception {
2336 		write(getTodoFile(), "");
2337 		assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, true).size());
2338 		assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
2339 	}
2340 
2341 	@Test
2342 	public void testOnlyCommentRebaseTodo() throws Exception {
2343 		write(getTodoFile(), "# a b c d e\n# e f");
2344 		assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
2345 		List<RebaseTodoLine> lines = db.readRebaseTodo(GIT_REBASE_TODO, true);
2346 		assertEquals(2, lines.size());
2347 		for (RebaseTodoLine line : lines)
2348 			assertEquals(Action.COMMENT, line.getAction());
2349 		write(getTodoFile(), "# a b c d e\n# e f\n");
2350 		assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
2351 		lines = db.readRebaseTodo(GIT_REBASE_TODO, true);
2352 		assertEquals(2, lines.size());
2353 		for (RebaseTodoLine line : lines)
2354 			assertEquals(Action.COMMENT, line.getAction());
2355 		write(getTodoFile(), " 	 \r\n# a b c d e\r\n# e f\r\n#");
2356 		assertEquals(0, db.readRebaseTodo(GIT_REBASE_TODO, false).size());
2357 		lines = db.readRebaseTodo(GIT_REBASE_TODO, true);
2358 		assertEquals(4, lines.size());
2359 		for (RebaseTodoLine line : lines)
2360 			assertEquals(Action.COMMENT, line.getAction());
2361 	}
2362 
2363 	@Test
2364 	public void testLeadingSpacesRebaseTodo() throws Exception {
2365 		String todo =	"  \t\t pick 1111111 Commit 1\n"
2366 					+ "\t\n"
2367 					+ "\treword 2222222 Commit 2\n";
2368 		write(getTodoFile(), todo);
2369 
2370 		List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);
2371 
2372 		assertEquals(2, steps.size());
2373 		assertEquals("1111111", steps.get(0).getCommit().name());
2374 		assertEquals("2222222", steps.get(1).getCommit().name());
2375 		assertEquals(Action.REWORD, steps.get(1).getAction());
2376 	}
2377 
2378 	@Test
2379 	public void testRebaseShouldTryToParseValidLineMarkedAsComment()
2380 			throws IOException {
2381 		String todo = "# pick 1111111 Valid line commented out with space\n"
2382 				+ "#edit 2222222 Valid line commented out without space\n"
2383 				+ "# pick invalidLine Comment line at end\n";
2384 		write(getTodoFile(), todo);
2385 
2386 		List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, true);
2387 		assertEquals(3, steps.size());
2388 
2389 		RebaseTodoLine firstLine = steps.get(0);
2390 
2391 		assertEquals("1111111", firstLine.getCommit().name());
2392 		assertEquals("Valid line commented out with space",
2393 				firstLine.getShortMessage());
2394 		assertEquals("comment", firstLine.getAction().toToken());
2395 
2396 		try {
2397 			firstLine.setAction(Action.PICK);
2398 			assertEquals("1111111", firstLine.getCommit().name());
2399 			assertEquals("pick", firstLine.getAction().toToken());
2400 		} catch (Exception e) {
2401 			fail("Valid parsable RebaseTodoLine that has been commented out should allow to change the action, but failed");
2402 		}
2403 
2404 		assertEquals("2222222", steps.get(1).getCommit().name());
2405 		assertEquals("comment", steps.get(1).getAction().toToken());
2406 
2407 		assertEquals(null, steps.get(2).getCommit());
2408 		assertEquals(null, steps.get(2).getShortMessage());
2409 		assertEquals("comment", steps.get(2).getAction().toToken());
2410 		assertEquals("# pick invalidLine Comment line at end", steps.get(2)
2411 				.getComment());
2412 		try {
2413 			steps.get(2).setAction(Action.PICK);
2414 			fail("A comment RebaseTodoLine that doesn't contain a valid parsable line should fail, but doesn't");
2415 		} catch (Exception e) {
2416 			// expected
2417 		}
2418 
2419 	}
2420 
2421 	@SuppressWarnings("unused")
2422 	@Test
2423 	public void testRebaseTodoLineSetComment() throws Exception {
2424 		try {
2425 			new RebaseTodoLine("This is a invalid comment");
2426 			fail("Constructing a comment line with invalid comment string should fail, but doesn't");
2427 		} catch (IllegalArgumentException e) {
2428 			// expected
2429 		}
2430 		RebaseTodoLine validCommentLine = new RebaseTodoLine(
2431 				"# This is a comment");
2432 		assertEquals(Action.COMMENT, validCommentLine.getAction());
2433 		assertEquals("# This is a comment", validCommentLine.getComment());
2434 
2435 		RebaseTodoLine actionLineToBeChanged = new RebaseTodoLine(Action.EDIT,
2436 				AbbreviatedObjectId.fromString("1111111"), "short Message");
2437 		assertEquals(null, actionLineToBeChanged.getComment());
2438 
2439 		try {
2440 			actionLineToBeChanged.setComment("invalid comment");
2441 			fail("Setting a invalid comment string should fail but doesn't");
2442 		} catch (IllegalArgumentException e) {
2443 			assertEquals(null, actionLineToBeChanged.getComment());
2444 		}
2445 
2446 		actionLineToBeChanged.setComment("# valid comment");
2447 		assertEquals("# valid comment", actionLineToBeChanged.getComment());
2448 		try {
2449 			actionLineToBeChanged.setComment("invalid comment");
2450 			fail("Setting a invalid comment string should fail but doesn't");
2451 		} catch (IllegalArgumentException e) {
2452 			// expected
2453 			// setting comment failed, but was successfully set before,
2454 			// therefore it may not be altered since then
2455 			assertEquals("# valid comment", actionLineToBeChanged.getComment());
2456 		}
2457 		try {
2458 			actionLineToBeChanged.setComment("# line1 \n line2");
2459 			actionLineToBeChanged.setComment("line1 \n line2");
2460 			actionLineToBeChanged.setComment("\n");
2461 			actionLineToBeChanged.setComment("# line1 \r line2");
2462 			actionLineToBeChanged.setComment("line1 \r line2");
2463 			actionLineToBeChanged.setComment("\r");
2464 			actionLineToBeChanged.setComment("# line1 \n\r line2");
2465 			actionLineToBeChanged.setComment("line1 \n\r line2");
2466 			actionLineToBeChanged.setComment("\n\r");
2467 			fail("Setting a multiline comment string should fail but doesn't");
2468 		} catch (IllegalArgumentException e) {
2469 			// expected
2470 		}
2471 		// Try setting valid comments
2472 		actionLineToBeChanged.setComment("# valid comment");
2473 		assertEquals("# valid comment", actionLineToBeChanged.getComment());
2474 
2475 		actionLineToBeChanged.setComment("# \t \t valid comment");
2476 		assertEquals("# \t \t valid comment",
2477 				actionLineToBeChanged.getComment());
2478 
2479 		actionLineToBeChanged.setComment("#       ");
2480 		assertEquals("#       ", actionLineToBeChanged.getComment());
2481 
2482 		actionLineToBeChanged.setComment("");
2483 		assertEquals("", actionLineToBeChanged.getComment());
2484 
2485 		actionLineToBeChanged.setComment("  ");
2486 		assertEquals("  ", actionLineToBeChanged.getComment());
2487 
2488 		actionLineToBeChanged.setComment("\t\t");
2489 		assertEquals("\t\t", actionLineToBeChanged.getComment());
2490 
2491 		actionLineToBeChanged.setComment(null);
2492 		assertEquals(null, actionLineToBeChanged.getComment());
2493 	}
2494 
2495 	@Test
2496 	public void testRebaseInteractiveReword() throws Exception {
2497 		// create file1 on master
2498 		writeTrashFile(FILE1, FILE1);
2499 		git.add().addFilepattern(FILE1).call();
2500 		git.commit().setMessage("Add file1").call();
2501 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2502 
2503 		// create file2 on master
2504 		writeTrashFile("file2", "file2");
2505 		git.add().addFilepattern("file2").call();
2506 		git.commit().setMessage("Add file2").call();
2507 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2508 
2509 		// update FILE1 on master
2510 		writeTrashFile(FILE1, "blah");
2511 		git.add().addFilepattern(FILE1).call();
2512 		git.commit().setMessage("updated file1 on master").call();
2513 
2514 		writeTrashFile("file2", "more change");
2515 		git.add().addFilepattern("file2").call();
2516 		git.commit().setMessage("update file2 on side").call();
2517 
2518 		RebaseResult res = git.rebase().setUpstream("HEAD~2")
2519 				.runInteractively(new InteractiveHandler() {
2520 
2521 					@Override
2522 					public void prepareSteps(List<RebaseTodoLine> steps) {
2523 						try {
2524 							steps.get(0).setAction(Action.REWORD);
2525 						} catch (IllegalTodoFileModification e) {
2526 							fail("unexpected exception: " + e);
2527 						}
2528 					}
2529 
2530 					@Override
2531 					public String modifyCommitMessage(String commit) {
2532 						return "rewritten commit message";
2533 					}
2534 				}).call();
2535 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2536 		checkFile(new File(db.getWorkTree(), "file2"), "more change");
2537 		assertEquals(Status.OK, res.getStatus());
2538 		Iterator<RevCommit> logIterator = git.log().all().call().iterator();
2539 		logIterator.next(); // skip first commit;
2540 		String actualCommitMag = logIterator.next().getShortMessage();
2541 		assertEquals("rewritten commit message", actualCommitMag);
2542 	}
2543 
2544 	@Test
2545 	public void testRebaseInteractiveEdit() throws Exception {
2546 		// create file1 on master
2547 		writeTrashFile(FILE1, FILE1);
2548 		git.add().addFilepattern(FILE1).call();
2549 		git.commit().setMessage("Add file1").call();
2550 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2551 
2552 		// create file2 on master
2553 		writeTrashFile("file2", "file2");
2554 		git.add().addFilepattern("file2").call();
2555 		git.commit().setMessage("Add file2").call();
2556 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2557 
2558 		// update FILE1 on master
2559 		writeTrashFile(FILE1, "blah");
2560 		git.add().addFilepattern(FILE1).call();
2561 		git.commit().setMessage("updated file1 on master").call();
2562 
2563 		writeTrashFile("file2", "more change");
2564 		git.add().addFilepattern("file2").call();
2565 		git.commit().setMessage("update file2 on side").call();
2566 
2567 		RebaseResult res = git.rebase().setUpstream("HEAD~2")
2568 				.runInteractively(new InteractiveHandler() {
2569 					@Override
2570 					public void prepareSteps(List<RebaseTodoLine> steps) {
2571 						try {
2572 							steps.get(0).setAction(Action.EDIT);
2573 						} catch (IllegalTodoFileModification e) {
2574 							fail("unexpected exception: " + e);
2575 						}
2576 					}
2577 
2578 					@Override
2579 					public String modifyCommitMessage(String commit) {
2580 						return ""; // not used
2581 					}
2582 				}).call();
2583 		assertEquals(Status.EDIT, res.getStatus());
2584 		RevCommit toBeEditted = git.log().call().iterator().next();
2585 		assertEquals("updated file1 on master", toBeEditted.getFullMessage());
2586 
2587 		// change file and commit with new commit message
2588 		writeTrashFile("file1", "edited");
2589 		git.commit().setAll(true).setAmend(true)
2590 				.setMessage("edited commit message").call();
2591 		// resume rebase
2592 		res = git.rebase().setOperation(Operation.CONTINUE).call();
2593 
2594 		checkFile(new File(db.getWorkTree(), "file1"), "edited");
2595 		assertEquals(Status.OK, res.getStatus());
2596 		Iterator<RevCommit> logIterator = git.log().all().call().iterator();
2597 		logIterator.next(); // skip first commit;
2598 		String actualCommitMag = logIterator.next().getShortMessage();
2599 		assertEquals("edited commit message", actualCommitMag);
2600 	}
2601 
2602 	@Test
2603 	public void testParseSquashFixupSequenceCount() {
2604 		int count = RebaseCommand
2605 				.parseSquashFixupSequenceCount("# This is a combination of 3 commits.\n# newline");
2606 		assertEquals(3, count);
2607 	}
2608 
2609 	@Test
2610 	public void testRebaseInteractiveSingleSquashAndModifyMessage() throws Exception {
2611 		// create file1 on master
2612 		writeTrashFile(FILE1, FILE1);
2613 		git.add().addFilepattern(FILE1).call();
2614 		git.commit().setMessage("Add file1\nnew line").call();
2615 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2616 
2617 		// create file2 on master
2618 		writeTrashFile("file2", "file2");
2619 		git.add().addFilepattern("file2").call();
2620 		git.commit().setMessage("Add file2\nnew line").call();
2621 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2622 
2623 		// update FILE1 on master
2624 		writeTrashFile(FILE1, "blah");
2625 		git.add().addFilepattern(FILE1).call();
2626 		git.commit().setMessage("updated file1 on master\nnew line").call();
2627 
2628 		writeTrashFile("file2", "more change");
2629 		git.add().addFilepattern("file2").call();
2630 		git.commit().setMessage("update file2 on master\nnew line").call();
2631 
2632 		git.rebase().setUpstream("HEAD~3")
2633 				.runInteractively(new InteractiveHandler() {
2634 
2635 					@Override
2636 					public void prepareSteps(List<RebaseTodoLine> steps) {
2637 						try {
2638 							steps.get(1).setAction(Action.SQUASH);
2639 						} catch (IllegalTodoFileModification e) {
2640 							fail("unexpected exception: " + e);
2641 						}
2642 					}
2643 
2644 					@Override
2645 					public String modifyCommitMessage(String commit) {
2646 						final File messageSquashFile = new File(db
2647 								.getDirectory(), "rebase-merge/message-squash");
2648 						final File messageFixupFile = new File(db
2649 								.getDirectory(), "rebase-merge/message-fixup");
2650 
2651 						assertFalse(messageFixupFile.exists());
2652 						assertTrue(messageSquashFile.exists());
2653 						assertEquals(
2654 								"# 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",
2655 								commit);
2656 
2657 						try {
2658 							byte[] messageSquashBytes = IO
2659 									.readFully(messageSquashFile);
2660 							int end = RawParseUtils.prevLF(messageSquashBytes,
2661 									messageSquashBytes.length);
2662 							String messageSquashContent = RawParseUtils.decode(
2663 									messageSquashBytes, 0, end + 1);
2664 							assertEquals(messageSquashContent, commit);
2665 						} catch (Throwable t) {
2666 							fail(t.getMessage());
2667 						}
2668 
2669 						return "changed";
2670 					}
2671 				}).call();
2672 
2673 		try (RevWalk walk = new RevWalk(db)) {
2674 			ObjectId headId = db.resolve(Constants.HEAD);
2675 			RevCommit headCommit = walk.parseCommit(headId);
2676 			assertEquals(headCommit.getFullMessage(),
2677 					"update file2 on master\nnew line");
2678 
2679 			ObjectId head2Id = db.resolve(Constants.HEAD + "^1");
2680 			RevCommit head1Commit = walk.parseCommit(head2Id);
2681 			assertEquals("changed", head1Commit.getFullMessage());
2682 		}
2683 	}
2684 
2685 	@Test
2686 	public void testRebaseInteractiveMultipleSquash() throws Exception {
2687 		// create file0 on master
2688 		writeTrashFile("file0", "file0");
2689 		git.add().addFilepattern("file0").call();
2690 		git.commit().setMessage("Add file0\nnew line").call();
2691 		assertTrue(new File(db.getWorkTree(), "file0").exists());
2692 
2693 		// create file1 on master
2694 		writeTrashFile(FILE1, FILE1);
2695 		git.add().addFilepattern(FILE1).call();
2696 		git.commit().setMessage("Add file1\nnew line").call();
2697 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2698 
2699 		// create file2 on master
2700 		writeTrashFile("file2", "file2");
2701 		git.add().addFilepattern("file2").call();
2702 		git.commit().setMessage("Add file2\nnew line").call();
2703 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2704 
2705 		// update FILE1 on master
2706 		writeTrashFile(FILE1, "blah");
2707 		git.add().addFilepattern(FILE1).call();
2708 		git.commit().setMessage("updated file1 on master\nnew line").call();
2709 
2710 		writeTrashFile("file2", "more change");
2711 		git.add().addFilepattern("file2").call();
2712 		git.commit().setMessage("update file2 on master\nnew line").call();
2713 
2714 		git.rebase().setUpstream("HEAD~4")
2715 				.runInteractively(new InteractiveHandler() {
2716 
2717 					@Override
2718 					public void prepareSteps(List<RebaseTodoLine> steps) {
2719 						try {
2720 							steps.get(1).setAction(Action.SQUASH);
2721 							steps.get(2).setAction(Action.SQUASH);
2722 						} catch (IllegalTodoFileModification e) {
2723 							fail("unexpected exception: " + e);
2724 						}
2725 					}
2726 
2727 					@Override
2728 					public String modifyCommitMessage(String commit) {
2729 						final File messageSquashFile = new File(db.getDirectory(),
2730 								"rebase-merge/message-squash");
2731 						final File messageFixupFile = new File(db.getDirectory(),
2732 								"rebase-merge/message-fixup");
2733 						assertFalse(messageFixupFile.exists());
2734 						assertTrue(messageSquashFile.exists());
2735 						assertEquals(
2736 								"# 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",
2737 								commit);
2738 
2739 						try {
2740 							byte[] messageSquashBytes = IO
2741 									.readFully(messageSquashFile);
2742 							int end = RawParseUtils.prevLF(messageSquashBytes,
2743 									messageSquashBytes.length);
2744 							String messageSquashContend = RawParseUtils.decode(
2745 									messageSquashBytes, 0, end + 1);
2746 							assertEquals(messageSquashContend, commit);
2747 						} catch (Throwable t) {
2748 							fail(t.getMessage());
2749 						}
2750 
2751 						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";
2752 					}
2753 				}).call();
2754 
2755 		try (RevWalk walk = new RevWalk(db)) {
2756 			ObjectId headId = db.resolve(Constants.HEAD);
2757 			RevCommit headCommit = walk.parseCommit(headId);
2758 			assertEquals(headCommit.getFullMessage(),
2759 					"update file2 on master\nnew line");
2760 
2761 			ObjectId head2Id = db.resolve(Constants.HEAD + "^1");
2762 			RevCommit head1Commit = walk.parseCommit(head2Id);
2763 			assertEquals(
2764 					"Add file1\nnew line\nAdd file2\nnew line\nupdated file1 on master\nnew line",
2765 					head1Commit.getFullMessage());
2766 		}
2767 	}
2768 
2769 	@Test
2770 	public void testRebaseInteractiveMixedSquashAndFixup() throws Exception {
2771 		// create file0 on master
2772 		writeTrashFile("file0", "file0");
2773 		git.add().addFilepattern("file0").call();
2774 		git.commit().setMessage("Add file0\nnew line").call();
2775 		assertTrue(new File(db.getWorkTree(), "file0").exists());
2776 
2777 		// create file1 on master
2778 		writeTrashFile(FILE1, FILE1);
2779 		git.add().addFilepattern(FILE1).call();
2780 		git.commit().setMessage("Add file1\nnew line").call();
2781 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2782 
2783 		// create file2 on master
2784 		writeTrashFile("file2", "file2");
2785 		git.add().addFilepattern("file2").call();
2786 		git.commit().setMessage("Add file2\nnew line").call();
2787 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2788 
2789 		// update FILE1 on master
2790 		writeTrashFile(FILE1, "blah");
2791 		git.add().addFilepattern(FILE1).call();
2792 		git.commit().setMessage("updated file1 on master\nnew line").call();
2793 
2794 		writeTrashFile("file2", "more change");
2795 		git.add().addFilepattern("file2").call();
2796 		git.commit().setMessage("update file2 on master\nnew line").call();
2797 
2798 		git.rebase().setUpstream("HEAD~4")
2799 				.runInteractively(new InteractiveHandler() {
2800 
2801 					@Override
2802 					public void prepareSteps(List<RebaseTodoLine> steps) {
2803 						try {
2804 							steps.get(1).setAction(Action.FIXUP);
2805 							steps.get(2).setAction(Action.SQUASH);
2806 						} catch (IllegalTodoFileModification e) {
2807 							fail("unexpected exception: " + e);
2808 						}
2809 					}
2810 
2811 					@Override
2812 					public String modifyCommitMessage(String commit) {
2813 						final File messageSquashFile = new File(db
2814 								.getDirectory(), "rebase-merge/message-squash");
2815 						final File messageFixupFile = new File(db
2816 								.getDirectory(), "rebase-merge/message-fixup");
2817 
2818 						assertFalse(messageFixupFile.exists());
2819 						assertTrue(messageSquashFile.exists());
2820 						assertEquals(
2821 								"# 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",
2822 								commit);
2823 
2824 						try {
2825 							byte[] messageSquashBytes = IO
2826 									.readFully(messageSquashFile);
2827 							int end = RawParseUtils.prevLF(messageSquashBytes,
2828 									messageSquashBytes.length);
2829 							String messageSquashContend = RawParseUtils.decode(
2830 									messageSquashBytes, 0, end + 1);
2831 							assertEquals(messageSquashContend, commit);
2832 						} catch (Throwable t) {
2833 							fail(t.getMessage());
2834 						}
2835 
2836 						return "changed";
2837 					}
2838 				}).call();
2839 
2840 		try (RevWalk walk = new RevWalk(db)) {
2841 			ObjectId headId = db.resolve(Constants.HEAD);
2842 			RevCommit headCommit = walk.parseCommit(headId);
2843 			assertEquals(headCommit.getFullMessage(),
2844 					"update file2 on master\nnew line");
2845 
2846 			ObjectId head2Id = db.resolve(Constants.HEAD + "^1");
2847 			RevCommit head1Commit = walk.parseCommit(head2Id);
2848 			assertEquals("changed", head1Commit.getFullMessage());
2849 		}
2850 	}
2851 
2852 	@Test
2853 	public void testRebaseInteractiveSingleFixup() throws Exception {
2854 		// create file1 on master
2855 		writeTrashFile(FILE1, FILE1);
2856 		git.add().addFilepattern(FILE1).call();
2857 		git.commit().setMessage("Add file1\nnew line").call();
2858 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2859 
2860 		// create file2 on master
2861 		writeTrashFile("file2", "file2");
2862 		git.add().addFilepattern("file2").call();
2863 		git.commit().setMessage("Add file2\nnew line").call();
2864 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2865 
2866 		// update FILE1 on master
2867 		writeTrashFile(FILE1, "blah");
2868 		git.add().addFilepattern(FILE1).call();
2869 		git.commit().setMessage("updated file1 on master\nnew line").call();
2870 
2871 		writeTrashFile("file2", "more change");
2872 		git.add().addFilepattern("file2").call();
2873 		git.commit().setMessage("update file2 on master\nnew line").call();
2874 
2875 		git.rebase().setUpstream("HEAD~3")
2876 				.runInteractively(new InteractiveHandler() {
2877 
2878 					@Override
2879 					public void prepareSteps(List<RebaseTodoLine> steps) {
2880 						try {
2881 							steps.get(1).setAction(Action.FIXUP);
2882 						} catch (IllegalTodoFileModification e) {
2883 							fail("unexpected exception: " + e);
2884 						}
2885 					}
2886 
2887 					@Override
2888 					public String modifyCommitMessage(String commit) {
2889 						fail("No callback to modify commit message expected for single fixup");
2890 						return commit;
2891 					}
2892 				}).call();
2893 
2894 		try (RevWalk walk = new RevWalk(db)) {
2895 			ObjectId headId = db.resolve(Constants.HEAD);
2896 			RevCommit headCommit = walk.parseCommit(headId);
2897 			assertEquals("update file2 on master\nnew line",
2898 					headCommit.getFullMessage());
2899 
2900 			ObjectId head1Id = db.resolve(Constants.HEAD + "^1");
2901 			RevCommit head1Commit = walk.parseCommit(head1Id);
2902 			assertEquals("Add file2\nnew line",
2903 					head1Commit.getFullMessage());
2904 		}
2905 	}
2906 
2907 	@Test
2908 	public void testRebaseInteractiveFixupWithBlankLines() throws Exception {
2909 		// create file1 on master
2910 		writeTrashFile(FILE1, FILE1);
2911 		git.add().addFilepattern(FILE1).call();
2912 		git.commit().setMessage("Add file1\nnew line").call();
2913 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2914 
2915 		// create file2 on master
2916 		writeTrashFile("file2", "file2");
2917 		git.add().addFilepattern("file2").call();
2918 		git.commit().setMessage("Add file2").call();
2919 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2920 
2921 		// update FILE1 on master
2922 		writeTrashFile(FILE1, "blah");
2923 		git.add().addFilepattern(FILE1).call();
2924 		git.commit().setMessage("updated file1 on master\n\nsome text").call();
2925 
2926 		git.rebase().setUpstream("HEAD~2")
2927 				.runInteractively(new InteractiveHandler() {
2928 
2929 					@Override
2930 					public void prepareSteps(List<RebaseTodoLine> steps) {
2931 						try {
2932 							steps.get(1).setAction(Action.FIXUP);
2933 						} catch (IllegalTodoFileModification e) {
2934 							fail("unexpected exception: " + e);
2935 						}
2936 					}
2937 
2938 					@Override
2939 					public String modifyCommitMessage(String commit) {
2940 						fail("No callback to modify commit message expected for single fixup");
2941 						return commit;
2942 					}
2943 				}).call();
2944 
2945 		try (RevWalk walk = new RevWalk(db)) {
2946 			ObjectId headId = db.resolve(Constants.HEAD);
2947 			RevCommit headCommit = walk.parseCommit(headId);
2948 			assertEquals("Add file2",
2949 					headCommit.getFullMessage());
2950 		}
2951 	}
2952 
2953 	@Test(expected = InvalidRebaseStepException.class)
2954 	public void testRebaseInteractiveFixupFirstCommitShouldFail()
2955 			throws Exception {
2956 		// create file1 on master
2957 		writeTrashFile(FILE1, FILE1);
2958 		git.add().addFilepattern(FILE1).call();
2959 		git.commit().setMessage("Add file1\nnew line").call();
2960 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2961 
2962 		// create file2 on master
2963 		writeTrashFile("file2", "file2");
2964 		git.add().addFilepattern("file2").call();
2965 		git.commit().setMessage("Add file2\nnew line").call();
2966 		assertTrue(new File(db.getWorkTree(), "file2").exists());
2967 
2968 		git.rebase().setUpstream("HEAD~1")
2969 				.runInteractively(new InteractiveHandler() {
2970 
2971 					@Override
2972 					public void prepareSteps(List<RebaseTodoLine> steps) {
2973 						try {
2974 							steps.get(0).setAction(Action.FIXUP);
2975 						} catch (IllegalTodoFileModification e) {
2976 							fail("unexpected exception: " + e);
2977 						}
2978 					}
2979 
2980 					@Override
2981 					public String modifyCommitMessage(String commit) {
2982 						return commit;
2983 					}
2984 				}).call();
2985 	}
2986 
2987 	@Test(expected = InvalidRebaseStepException.class)
2988 	public void testRebaseInteractiveSquashFirstCommitShouldFail()
2989 			throws Exception {
2990 		// create file1 on master
2991 		writeTrashFile(FILE1, FILE1);
2992 		git.add().addFilepattern(FILE1).call();
2993 		git.commit().setMessage("Add file1\nnew line").call();
2994 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
2995 
2996 		// create file2 on master
2997 		writeTrashFile("file2", "file2");
2998 		git.add().addFilepattern("file2").call();
2999 		git.commit().setMessage("Add file2\nnew line").call();
3000 		assertTrue(new File(db.getWorkTree(), "file2").exists());
3001 
3002 		git.rebase().setUpstream("HEAD~1")
3003 				.runInteractively(new InteractiveHandler() {
3004 
3005 					@Override
3006 					public void prepareSteps(List<RebaseTodoLine> steps) {
3007 						try {
3008 							steps.get(0).setAction(Action.SQUASH);
3009 						} catch (IllegalTodoFileModification e) {
3010 							fail("unexpected exception: " + e);
3011 						}
3012 					}
3013 
3014 					@Override
3015 					public String modifyCommitMessage(String commit) {
3016 						return commit;
3017 					}
3018 				}).call();
3019 	}
3020 
3021 	@Test
3022 	public void testRebaseEndsIfLastStepIsEdit() throws Exception {
3023 		// create file1 on master
3024 		writeTrashFile(FILE1, FILE1);
3025 		git.add().addFilepattern(FILE1).call();
3026 		git.commit().setMessage("Add file1\nnew line").call();
3027 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
3028 
3029 		// create file2 on master
3030 		writeTrashFile("file2", "file2");
3031 		git.add().addFilepattern("file2").call();
3032 		git.commit().setMessage("Add file2\nnew line").call();
3033 		assertTrue(new File(db.getWorkTree(), "file2").exists());
3034 
3035 		git.rebase().setUpstream("HEAD~1")
3036 				.runInteractively(new InteractiveHandler() {
3037 
3038 					@Override
3039 					public void prepareSteps(List<RebaseTodoLine> steps) {
3040 						try {
3041 							steps.get(0).setAction(Action.EDIT);
3042 						} catch (IllegalTodoFileModification e) {
3043 							fail("unexpected exception: " + e);
3044 						}
3045 					}
3046 
3047 					@Override
3048 					public String modifyCommitMessage(String commit) {
3049 						return commit;
3050 					}
3051 				}).call();
3052 		git.commit().setAmend(true)
3053 				.setMessage("Add file2\nnew line\nanother line").call();
3054 		RebaseResult result = git.rebase().setOperation(Operation.CONTINUE)
3055 				.call();
3056 		assertEquals(Status.OK, result.getStatus());
3057 
3058 	}
3059 
3060 	@Test
3061 	public void testRebaseShouldStopForEditInCaseOfConflict()
3062 			throws Exception {
3063 		// create file1 on master
3064 		writeTrashFile(FILE1, FILE1);
3065 		git.add().addFilepattern(FILE1).call();
3066 		git.commit().setMessage("Add file1\nnew line").call();
3067 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
3068 
3069 		//change file1
3070 		writeTrashFile(FILE1, FILE1 + "a");
3071 		git.add().addFilepattern(FILE1).call();
3072 		git.commit().setMessage("Change file1").call();
3073 
3074 		//change file1
3075 		writeTrashFile(FILE1, FILE1 + "b");
3076 		git.add().addFilepattern(FILE1).call();
3077 		git.commit().setMessage("Change file1").call();
3078 
3079 		RebaseResult result = git.rebase().setUpstream("HEAD~2")
3080 				.runInteractively(new InteractiveHandler() {
3081 
3082 					@Override
3083 					public void prepareSteps(List<RebaseTodoLine> steps) {
3084 						steps.remove(0);
3085 						try {
3086 							steps.get(0).setAction(Action.EDIT);
3087 						} catch (IllegalTodoFileModification e) {
3088 							fail("unexpected exception: " + e);
3089 						}
3090 					}
3091 
3092 					@Override
3093 					public String modifyCommitMessage(String commit) {
3094 						return commit;
3095 					}
3096 				}).call();
3097 		assertEquals(Status.STOPPED, result.getStatus());
3098 		git.add().addFilepattern(FILE1).call();
3099 		result = git.rebase().setOperation(Operation.CONTINUE).call();
3100 		assertEquals(Status.EDIT, result.getStatus());
3101 
3102 	}
3103 
3104 	@Test
3105 	public void testRebaseShouldStopForRewordInCaseOfConflict()
3106 			throws Exception {
3107 		// create file1 on master
3108 		writeTrashFile(FILE1, FILE1);
3109 		git.add().addFilepattern(FILE1).call();
3110 		git.commit().setMessage("Add file1\nnew line").call();
3111 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
3112 
3113 		// change file1
3114 		writeTrashFile(FILE1, FILE1 + "a");
3115 		git.add().addFilepattern(FILE1).call();
3116 		git.commit().setMessage("Change file1").call();
3117 
3118 		// change file1
3119 		writeTrashFile(FILE1, FILE1 + "b");
3120 		git.add().addFilepattern(FILE1).call();
3121 		git.commit().setMessage("Change file1").call();
3122 
3123 		RebaseResult result = git.rebase().setUpstream("HEAD~2")
3124 				.runInteractively(new InteractiveHandler() {
3125 
3126 					@Override
3127 					public void prepareSteps(List<RebaseTodoLine> steps) {
3128 						steps.remove(0);
3129 						try {
3130 							steps.get(0).setAction(Action.REWORD);
3131 						} catch (IllegalTodoFileModification e) {
3132 							fail("unexpected exception: " + e);
3133 						}
3134 					}
3135 
3136 					@Override
3137 					public String modifyCommitMessage(String commit) {
3138 						return "rewritten commit message";
3139 					}
3140 				}).call();
3141 		assertEquals(Status.STOPPED, result.getStatus());
3142 		git.add().addFilepattern(FILE1).call();
3143 		result = git.rebase().runInteractively(new InteractiveHandler() {
3144 
3145 			@Override
3146 			public void prepareSteps(List<RebaseTodoLine> steps) {
3147 				steps.remove(0);
3148 				try {
3149 					steps.get(0).setAction(Action.REWORD);
3150 				} catch (IllegalTodoFileModification e) {
3151 					fail("unexpected exception: " + e);
3152 				}
3153 			}
3154 
3155 			@Override
3156 			public String modifyCommitMessage(String commit) {
3157 				return "rewritten commit message";
3158 			}
3159 		}).setOperation(Operation.CONTINUE).call();
3160 		assertEquals(Status.OK, result.getStatus());
3161 		Iterator<RevCommit> logIterator = git.log().all().call().iterator();
3162 		String actualCommitMag = logIterator.next().getShortMessage();
3163 		assertEquals("rewritten commit message", actualCommitMag);
3164 
3165 	}
3166 
3167 	@Test
3168 	public void testRebaseShouldSquashInCaseOfConflict() throws Exception {
3169 		// create file1 on master
3170 		writeTrashFile(FILE1, FILE1);
3171 		git.add().addFilepattern(FILE1).call();
3172 		git.commit().setMessage("Add file1\nnew line").call();
3173 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
3174 
3175 		// change file2
3176 		writeTrashFile("file2", "file2");
3177 		git.add().addFilepattern("file2").call();
3178 		git.commit().setMessage("Change file2").call();
3179 
3180 		// change file1
3181 		writeTrashFile(FILE1, FILE1 + "a");
3182 		git.add().addFilepattern(FILE1).call();
3183 		git.commit().setMessage("Change file1").call();
3184 
3185 		// change file1
3186 		writeTrashFile(FILE1, FILE1 + "b");
3187 		git.add().addFilepattern(FILE1).call();
3188 		git.commit().setMessage("Change file1").call();
3189 
3190 		RebaseResult result = git.rebase().setUpstream("HEAD~3")
3191 				.runInteractively(new InteractiveHandler() {
3192 
3193 					@Override
3194 					public void prepareSteps(List<RebaseTodoLine> steps) {
3195 						try {
3196 							steps.get(0).setAction(Action.PICK);
3197 							steps.remove(1);
3198 							steps.get(1).setAction(Action.SQUASH);
3199 						} catch (IllegalTodoFileModification e) {
3200 							fail("unexpected exception: " + e);
3201 						}
3202 					}
3203 
3204 					@Override
3205 					public String modifyCommitMessage(String commit) {
3206 						return "squashed message";
3207 					}
3208 				}).call();
3209 		assertEquals(Status.STOPPED, result.getStatus());
3210 		git.add().addFilepattern(FILE1).call();
3211 		result = git.rebase().runInteractively(new InteractiveHandler() {
3212 
3213 			@Override
3214 			public void prepareSteps(List<RebaseTodoLine> steps) {
3215 				try {
3216 					steps.get(0).setAction(Action.PICK);
3217 					steps.remove(1);
3218 					steps.get(1).setAction(Action.SQUASH);
3219 				} catch (IllegalTodoFileModification e) {
3220 					fail("unexpected exception: " + e);
3221 				}
3222 			}
3223 
3224 			@Override
3225 			public String modifyCommitMessage(String commit) {
3226 				return "squashed message";
3227 			}
3228 		}).setOperation(Operation.CONTINUE).call();
3229 		assertEquals(Status.OK, result.getStatus());
3230 		Iterator<RevCommit> logIterator = git.log().all().call().iterator();
3231 		String actualCommitMag = logIterator.next().getShortMessage();
3232 		assertEquals("squashed message", actualCommitMag);
3233 	}
3234 
3235 	@Test
3236 	public void testRebaseShouldFixupInCaseOfConflict() throws Exception {
3237 		// create file1 on master
3238 		writeTrashFile(FILE1, FILE1);
3239 		git.add().addFilepattern(FILE1).call();
3240 		git.commit().setMessage("Add file1").call();
3241 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
3242 
3243 		// change file2
3244 		writeTrashFile("file2", "file2");
3245 		git.add().addFilepattern("file2").call();
3246 		git.commit().setMessage("Change file2").call();
3247 
3248 		// change file1
3249 		writeTrashFile(FILE1, FILE1 + "a");
3250 		git.add().addFilepattern(FILE1).call();
3251 		git.commit().setMessage("Change file1").call();
3252 
3253 		// change file1, add file3
3254 		writeTrashFile(FILE1, FILE1 + "b");
3255 		writeTrashFile("file3", "file3");
3256 		git.add().addFilepattern(FILE1).call();
3257 		git.add().addFilepattern("file3").call();
3258 		git.commit().setMessage("Change file1, add file3").call();
3259 
3260 		RebaseResult result = git.rebase().setUpstream("HEAD~3")
3261 				.runInteractively(new InteractiveHandler() {
3262 
3263 					@Override
3264 					public void prepareSteps(List<RebaseTodoLine> steps) {
3265 						try {
3266 							steps.get(0).setAction(Action.PICK);
3267 							steps.remove(1);
3268 							steps.get(1).setAction(Action.FIXUP);
3269 						} catch (IllegalTodoFileModification e) {
3270 							fail("unexpected exception: " + e);
3271 						}
3272 					}
3273 
3274 					@Override
3275 					public String modifyCommitMessage(String commit) {
3276 						return commit;
3277 					}
3278 				}).call();
3279 		assertEquals(Status.STOPPED, result.getStatus());
3280 		git.add().addFilepattern(FILE1).call();
3281 		result = git.rebase().runInteractively(new InteractiveHandler() {
3282 
3283 			@Override
3284 			public void prepareSteps(List<RebaseTodoLine> steps) {
3285 				try {
3286 					steps.get(0).setAction(Action.PICK);
3287 					steps.remove(1);
3288 					steps.get(1).setAction(Action.FIXUP);
3289 				} catch (IllegalTodoFileModification e) {
3290 					fail("unexpected exception: " + e);
3291 				}
3292 			}
3293 
3294 			@Override
3295 			public String modifyCommitMessage(String commit) {
3296 				return "commit";
3297 			}
3298 		}).setOperation(Operation.CONTINUE).call();
3299 		assertEquals(Status.OK, result.getStatus());
3300 		Iterator<RevCommit> logIterator = git.log().all().call().iterator();
3301 		String actualCommitMsg = logIterator.next().getShortMessage();
3302 		assertEquals("Change file2", actualCommitMsg);
3303 		actualCommitMsg = logIterator.next().getShortMessage();
3304 		assertEquals("Add file1", actualCommitMsg);
3305 		assertTrue(new File(db.getWorkTree(), "file3").exists());
3306 
3307 	}
3308 
3309 	@Test
3310 	public void testInteractiveRebaseWithModificationShouldNotDeleteDataOnAbort()
3311 			throws Exception {
3312 		// create file0 + file1, add and commit
3313 		writeTrashFile("file0", "file0");
3314 		writeTrashFile(FILE1, "file1");
3315 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
3316 		git.commit().setMessage("commit1").call();
3317 
3318 		// modify file1, add and commit
3319 		writeTrashFile(FILE1, "modified file1");
3320 		git.add().addFilepattern(FILE1).call();
3321 		git.commit().setMessage("commit2").call();
3322 
3323 		// modify file1, add and commit
3324 		writeTrashFile(FILE1, "modified file1 a second time");
3325 		git.add().addFilepattern(FILE1).call();
3326 		git.commit().setMessage("commit3").call();
3327 
3328 		// modify file0, but do not commit
3329 		writeTrashFile("file0", "modified file0 in index");
3330 		git.add().addFilepattern("file0").addFilepattern(FILE1).call();
3331 		// do not commit
3332 		writeTrashFile("file0", "modified file0");
3333 
3334 		// start rebase
3335 		RebaseResult result = git.rebase().setUpstream("HEAD~2")
3336 				.runInteractively(new InteractiveHandler() {
3337 
3338 					@Override
3339 					public void prepareSteps(List<RebaseTodoLine> steps) {
3340 						try {
3341 							steps.get(0).setAction(Action.EDIT);
3342 							steps.get(1).setAction(Action.PICK);
3343 						} catch (IllegalTodoFileModification e) {
3344 							fail("unexpected exception: " + e);
3345 						}
3346 					}
3347 
3348 					@Override
3349 					public String modifyCommitMessage(String commit) {
3350 						return commit;
3351 					}
3352 				}).call();
3353 		// the following condition was true before commit 83b6ab233:
3354 		// jgit started the rebase and deleted the change on abort
3355 		// This test should verify that content was deleted
3356 		if (result.getStatus() == Status.EDIT)
3357 			git.rebase().setOperation(Operation.ABORT).call();
3358 
3359 		checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
3360 		checkFile(new File(db.getWorkTree(), "file1"),
3361 				"modified file1 a second time");
3362 		assertEquals("[file0, mode:100644, content:modified file0 in index]"
3363 				+ "[file1, mode:100644, content:modified file1 a second time]",
3364 				indexState(CONTENT));
3365 
3366 	}
3367 
3368 	private File getTodoFile() {
3369 		File todoFile = new File(db.getDirectory(), GIT_REBASE_TODO);
3370 		return todoFile;
3371 	}
3372 }