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