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