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