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