1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.merge;
44
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertFalse;
47 import static org.junit.Assert.assertTrue;
48
49 import java.io.BufferedReader;
50 import java.io.File;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.io.InputStreamReader;
54
55 import org.eclipse.jgit.api.Git;
56 import org.eclipse.jgit.dircache.DirCache;
57 import org.eclipse.jgit.dircache.DirCacheEditor;
58 import org.eclipse.jgit.dircache.DirCacheEntry;
59 import org.eclipse.jgit.errors.MissingObjectException;
60 import org.eclipse.jgit.errors.NoMergeBaseException;
61 import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
62 import org.eclipse.jgit.internal.storage.file.FileRepository;
63 import org.eclipse.jgit.junit.RepositoryTestCase;
64 import org.eclipse.jgit.junit.TestRepository;
65 import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
66 import org.eclipse.jgit.lib.AnyObjectId;
67 import org.eclipse.jgit.lib.Constants;
68 import org.eclipse.jgit.lib.FileMode;
69 import org.eclipse.jgit.lib.ObjectId;
70 import org.eclipse.jgit.lib.ObjectLoader;
71 import org.eclipse.jgit.lib.ObjectReader;
72 import org.eclipse.jgit.lib.Repository;
73 import org.eclipse.jgit.revwalk.RevBlob;
74 import org.eclipse.jgit.revwalk.RevCommit;
75 import org.eclipse.jgit.treewalk.FileTreeIterator;
76 import org.eclipse.jgit.treewalk.TreeWalk;
77 import org.eclipse.jgit.treewalk.filter.PathFilter;
78 import org.junit.Before;
79 import org.junit.experimental.theories.DataPoints;
80 import org.junit.experimental.theories.Theories;
81 import org.junit.experimental.theories.Theory;
82 import org.junit.runner.RunWith;
83
84 @RunWith(Theories.class)
85 public class RecursiveMergerTest extends RepositoryTestCase {
86 static int counter = 0;
87
88 @DataPoints
89 public static MergeStrategy[] strategiesUnderTest = new MergeStrategy[] {
90 MergeStrategy.RECURSIVE, MergeStrategy.RESOLVE };
91
92 public enum IndexState {
93 Bare, Missing, SameAsHead, SameAsOther, SameAsWorkTree, DifferentFromHeadAndOtherAndWorktree
94 }
95
96 @DataPoints
97 public static IndexState[] indexStates = IndexState.values();
98
99 public enum WorktreeState {
100 Bare, Missing, SameAsHead, DifferentFromHeadAndOther, SameAsOther;
101 }
102
103 @DataPoints
104 public static WorktreeState[] worktreeStates = WorktreeState.values();
105
106 private TestRepository<FileRepository> db_t;
107
108 @Override
109 @Before
110 public void setUp() throws Exception {
111 super.setUp();
112 db_t = new TestRepository<FileRepository>(db);
113 }
114
115 @Theory
116
117
118
119
120
121
122
123
124
125
126
127 public void crissCrossMerge(MergeStrategy strategy, IndexState indexState,
128 WorktreeState worktreeState) throws Exception {
129 if (!validateStates(indexState, worktreeState))
130 return;
131
132 BranchBuilder master = db_t.branch("master");
133 RevCommit m0 = master.commit().add("m", ",m0").message("m0").create();
134 RevCommit m1 = master.commit().add("m", "m1").message("m1").create();
135 db_t.getRevWalk().parseCommit(m1);
136
137 BranchBuilder side = db_t.branch("side");
138 RevCommit s1 = side.commit().parent(m0).add("s", "s1").message("s1")
139 .create();
140 RevCommit s2 = side.commit().parent(m1).add("m", "m1")
141 .message("s2(merge)").create();
142 RevCommit m2 = master.commit().parent(s1).add("s", "s1")
143 .message("m2(merge)").create();
144
145 Git git = Git.wrap(db);
146 git.checkout().setName("master").call();
147 modifyWorktree(worktreeState, "m", "side");
148 modifyWorktree(worktreeState, "s", "side");
149 modifyIndex(indexState, "m", "side");
150 modifyIndex(indexState, "s", "side");
151
152 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
153 worktreeState == WorktreeState.Bare);
154 if (worktreeState != WorktreeState.Bare)
155 merger.setWorkingTreeIterator(new FileTreeIterator(db));
156 try {
157 boolean expectSuccess = true;
158 if (!(indexState == IndexState.Bare
159 || indexState == IndexState.Missing
160 || indexState == IndexState.SameAsHead || indexState == IndexState.SameAsOther))
161
162 expectSuccess = false;
163
164 assertEquals(Boolean.valueOf(expectSuccess),
165 Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
166 assertEquals(MergeStrategy.RECURSIVE, strategy);
167 assertEquals("m1",
168 contentAsString(db, merger.getResultTreeId(), "m"));
169 assertEquals("s1",
170 contentAsString(db, merger.getResultTreeId(), "s"));
171 } catch (NoMergeBaseException e) {
172 assertEquals(MergeStrategy.RESOLVE, strategy);
173 assertEquals(e.getReason(),
174 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
175 }
176 }
177
178 @Theory
179
180
181
182
183
184
185
186
187
188
189
190
191 public void crissCrossMerge_twoRoots(MergeStrategy strategy,
192 IndexState indexState, WorktreeState worktreeState)
193 throws Exception {
194 if (!validateStates(indexState, worktreeState))
195 return;
196
197 BranchBuilder master = db_t.branch("master");
198 BranchBuilder side = db_t.branch("side");
199 RevCommit m1 = master.commit().add("m", "m1").message("m1").create();
200 db_t.getRevWalk().parseCommit(m1);
201
202 RevCommit s1 = side.commit().add("s", "s1").message("s1").create();
203 RevCommit s2 = side.commit().parent(m1).add("m", "m1")
204 .message("s2(merge)").create();
205 RevCommit m2 = master.commit().parent(s1).add("s", "s1")
206 .message("m2(merge)").create();
207
208 Git git = Git.wrap(db);
209 git.checkout().setName("master").call();
210 modifyWorktree(worktreeState, "m", "side");
211 modifyWorktree(worktreeState, "s", "side");
212 modifyIndex(indexState, "m", "side");
213 modifyIndex(indexState, "s", "side");
214
215 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
216 worktreeState == WorktreeState.Bare);
217 if (worktreeState != WorktreeState.Bare)
218 merger.setWorkingTreeIterator(new FileTreeIterator(db));
219 try {
220 boolean expectSuccess = true;
221 if (!(indexState == IndexState.Bare
222 || indexState == IndexState.Missing
223 || indexState == IndexState.SameAsHead || indexState == IndexState.SameAsOther))
224
225 expectSuccess = false;
226
227 assertEquals(Boolean.valueOf(expectSuccess),
228 Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
229 assertEquals(MergeStrategy.RECURSIVE, strategy);
230 assertEquals("m1",
231 contentAsString(db, merger.getResultTreeId(), "m"));
232 assertEquals("s1",
233 contentAsString(db, merger.getResultTreeId(), "s"));
234 } catch (NoMergeBaseException e) {
235 assertEquals(MergeStrategy.RESOLVE, strategy);
236 assertEquals(e.getReason(),
237 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
238 }
239 }
240
241 @Theory
242
243
244
245
246
247
248
249
250
251
252
253
254 public void crissCrossMerge_mergeable(MergeStrategy strategy,
255 IndexState indexState, WorktreeState worktreeState)
256 throws Exception {
257 if (!validateStates(indexState, worktreeState))
258 return;
259
260 BranchBuilder master = db_t.branch("master");
261 RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
262 .message("m0").create();
263 RevCommit m1 = master.commit()
264 .add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m1")
265 .create();
266 db_t.getRevWalk().parseCommit(m1);
267
268 BranchBuilder side = db_t.branch("side");
269 RevCommit s1 = side.commit().parent(m0)
270 .add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1")
271 .create();
272 RevCommit s2 = side.commit().parent(m1)
273 .add("f", "1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n")
274 .message("s2(merge)").create();
275 RevCommit m2 = master
276 .commit()
277 .parent(s1)
278 .add("f", "1-master\n2\n3-res(master)\n4\n5\n6\n7\n8\n9-side\n")
279 .message("m2(merge)").create();
280
281 Git git = Git.wrap(db);
282 git.checkout().setName("master").call();
283 modifyWorktree(worktreeState, "f", "side");
284 modifyIndex(indexState, "f", "side");
285
286 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
287 worktreeState == WorktreeState.Bare);
288 if (worktreeState != WorktreeState.Bare)
289 merger.setWorkingTreeIterator(new FileTreeIterator(db));
290 try {
291 boolean expectSuccess = true;
292 if (!(indexState == IndexState.Bare
293 || indexState == IndexState.Missing || indexState == IndexState.SameAsHead))
294
295 expectSuccess = false;
296 else if (worktreeState == WorktreeState.DifferentFromHeadAndOther
297 || worktreeState == WorktreeState.SameAsOther)
298 expectSuccess = false;
299 assertEquals(Boolean.valueOf(expectSuccess),
300 Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
301 assertEquals(MergeStrategy.RECURSIVE, strategy);
302 if (!expectSuccess)
303
304 return;
305 assertEquals(
306 "1-master\n2\n3-res(master)\n4\n5\n6\n7-res(side)\n8\n9-side",
307 contentAsString(db, merger.getResultTreeId(), "f"));
308 if (indexState != IndexState.Bare)
309 assertEquals(
310 "[f, mode:100644, content:1-master\n2\n3-res(master)\n4\n5\n6\n7-res(side)\n8\n9-side\n]",
311 indexState(RepositoryTestCase.CONTENT));
312 if (worktreeState != WorktreeState.Bare
313 && worktreeState != WorktreeState.Missing)
314 assertEquals(
315 "1-master\n2\n3-res(master)\n4\n5\n6\n7-res(side)\n8\n9-side\n",
316 read("f"));
317 } catch (NoMergeBaseException e) {
318 assertEquals(MergeStrategy.RESOLVE, strategy);
319 assertEquals(e.getReason(),
320 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
321 }
322 }
323
324 @Theory
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339 public void crissCrossMerge_mergeable2(MergeStrategy strategy,
340 IndexState indexState, WorktreeState worktreeState)
341 throws Exception {
342 if (!validateStates(indexState, worktreeState))
343 return;
344
345 BranchBuilder master = db_t.branch("master");
346 RevCommit m0 = master.commit().add("f", "1\n2\n3\n")
347 .message("m0")
348 .create();
349 RevCommit m1 = master.commit().add("f", "1-master\n2\n3\n")
350 .message("m1").create();
351 db_t.getRevWalk().parseCommit(m1);
352
353 BranchBuilder side = db_t.branch("side");
354 RevCommit s1 = side.commit().parent(m0).add("f", "1\n2\n3-side\n")
355 .message("s1").create();
356 RevCommit s2 = side.commit().parent(m1)
357 .add("f", "1-master\n2\n3-side-r\n")
358 .message("s2(merge)")
359 .create();
360 RevCommit m2 = master.commit().parent(s1)
361 .add("f", "1-master-r\n2\n3-side\n")
362 .message("m2(merge)")
363 .create();
364
365 Git git = Git.wrap(db);
366 git.checkout().setName("master").call();
367 modifyWorktree(worktreeState, "f", "side");
368 modifyIndex(indexState, "f", "side");
369
370 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
371 worktreeState == WorktreeState.Bare);
372 if (worktreeState != WorktreeState.Bare)
373 merger.setWorkingTreeIterator(new FileTreeIterator(db));
374 try {
375 boolean expectSuccess = true;
376 if (!(indexState == IndexState.Bare
377 || indexState == IndexState.Missing || indexState == IndexState.SameAsHead))
378
379 expectSuccess = false;
380 else if (worktreeState == WorktreeState.DifferentFromHeadAndOther
381 || worktreeState == WorktreeState.SameAsOther)
382 expectSuccess = false;
383 assertEquals(Boolean.valueOf(expectSuccess),
384 Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
385 assertEquals(MergeStrategy.RECURSIVE, strategy);
386 if (!expectSuccess)
387
388
389 return;
390 assertEquals(
391 "1-master-r\n2\n3-side-r",
392 contentAsString(db, merger.getResultTreeId(), "f"));
393 if (indexState != IndexState.Bare)
394 assertEquals(
395 "[f, mode:100644, content:1-master-r\n2\n3-side-r\n]",
396 indexState(RepositoryTestCase.CONTENT));
397 if (worktreeState != WorktreeState.Bare
398 && worktreeState != WorktreeState.Missing)
399 assertEquals(
400 "1-master-r\n2\n3-side-r\n",
401 read("f"));
402 } catch (NoMergeBaseException e) {
403 assertEquals(MergeStrategy.RESOLVE, strategy);
404 assertEquals(e.getReason(),
405 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
406 }
407 }
408
409 @Theory
410
411
412
413
414
415
416
417
418
419
420
421
422
423 public void crissCrossMerge_ParentsNotMergeable(MergeStrategy strategy,
424 IndexState indexState, WorktreeState worktreeState)
425 throws Exception {
426 if (!validateStates(indexState, worktreeState))
427 return;
428
429 BranchBuilder master = db_t.branch("master");
430 RevCommit m0 = master.commit().add("f", "1\n2\n3\n").message("m0")
431 .create();
432 RevCommit m1 = master.commit().add("f", "1\nx(master)\n2\n3\n")
433 .message("m1").create();
434 db_t.getRevWalk().parseCommit(m1);
435
436 BranchBuilder side = db_t.branch("side");
437 RevCommit s1 = side.commit().parent(m0)
438 .add("f", "1\nx(side)\n2\n3\ny(side)\n")
439 .message("s1").create();
440 RevCommit s2 = side.commit().parent(m1)
441 .add("f", "1\nx(side)\n2\n3\ny(side-again)\n")
442 .message("s2(merge)")
443 .create();
444 RevCommit m2 = master.commit().parent(s1)
445 .add("f", "1\nx(side)\n2\n3\ny(side)\n").message("m2(merge)")
446 .create();
447
448 Git git = Git.wrap(db);
449 git.checkout().setName("master").call();
450 modifyWorktree(worktreeState, "f", "side");
451 modifyIndex(indexState, "f", "side");
452
453 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
454 worktreeState == WorktreeState.Bare);
455 if (worktreeState != WorktreeState.Bare)
456 merger.setWorkingTreeIterator(new FileTreeIterator(db));
457 try {
458 boolean expectSuccess = true;
459 if (!(indexState == IndexState.Bare
460 || indexState == IndexState.Missing || indexState == IndexState.SameAsHead))
461
462 expectSuccess = false;
463 else if (worktreeState == WorktreeState.DifferentFromHeadAndOther
464 || worktreeState == WorktreeState.SameAsOther)
465 expectSuccess = false;
466 assertEquals("Merge didn't return as expected: strategy:"
467 + strategy.getName() + ", indexState:" + indexState
468 + ", worktreeState:" + worktreeState + " . ",
469 Boolean.valueOf(expectSuccess),
470 Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
471 assertEquals(MergeStrategy.RECURSIVE, strategy);
472 if (!expectSuccess)
473
474
475 return;
476 assertEquals("1\nx(side)\n2\n3\ny(side-again)",
477 contentAsString(db, merger.getResultTreeId(), "f"));
478 if (indexState != IndexState.Bare)
479 assertEquals(
480 "[f, mode:100644, content:1\nx(side)\n2\n3\ny(side-again)\n]",
481 indexState(RepositoryTestCase.CONTENT));
482 if (worktreeState != WorktreeState.Bare
483 && worktreeState != WorktreeState.Missing)
484 assertEquals("1\nx(side)\n2\n3\ny(side-again)\n", read("f"));
485 } catch (NoMergeBaseException e) {
486 assertEquals(MergeStrategy.RESOLVE, strategy);
487 assertEquals(e.getReason(),
488 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
489 }
490 }
491
492 @Theory
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507 public void crissCrossMerge_checkOtherFiles(MergeStrategy strategy,
508 IndexState indexState, WorktreeState worktreeState)
509 throws Exception {
510 if (!validateStates(indexState, worktreeState))
511 return;
512
513 BranchBuilder master = db_t.branch("master");
514 RevCommit m0 = master.commit().add("f", "1\n2\n3\n").add("m.m", "0")
515 .add("m.d", "0").add("s.m", "0").add("s.d", "0").message("m0")
516 .create();
517 RevCommit m1 = master.commit().add("f", "1-master\n2\n3\n")
518 .add("m.c", "0").add("m.m", "1").rm("m.d").message("m1")
519 .create();
520 db_t.getRevWalk().parseCommit(m1);
521
522 BranchBuilder side = db_t.branch("side");
523 RevCommit s1 = side.commit().parent(m0).add("f", "1\n2\n3-side\n")
524 .add("s.c", "0").add("s.m", "1").rm("s.d").message("s1")
525 .create();
526 RevCommit s2 = side.commit().parent(m1)
527 .add("f", "1-master\n2\n3-side-r\n").add("m.m", "1")
528 .add("m.c", "0").rm("m.d").message("s2(merge)").create();
529 RevCommit m2 = master.commit().parent(s1)
530 .add("f", "1-master-r\n2\n3-side\n").add("s.m", "1")
531 .add("s.c", "0").rm("s.d").message("m2(merge)").create();
532
533 Git git = Git.wrap(db);
534 git.checkout().setName("master").call();
535 modifyWorktree(worktreeState, "f", "side");
536 modifyIndex(indexState, "f", "side");
537
538 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
539 worktreeState == WorktreeState.Bare);
540 if (worktreeState != WorktreeState.Bare)
541 merger.setWorkingTreeIterator(new FileTreeIterator(db));
542 try {
543 boolean expectSuccess = true;
544 if (!(indexState == IndexState.Bare
545 || indexState == IndexState.Missing || indexState == IndexState.SameAsHead))
546
547 expectSuccess = false;
548 else if (worktreeState == WorktreeState.DifferentFromHeadAndOther
549 || worktreeState == WorktreeState.SameAsOther)
550 expectSuccess = false;
551 assertEquals(Boolean.valueOf(expectSuccess),
552 Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
553 assertEquals(MergeStrategy.RECURSIVE, strategy);
554 if (!expectSuccess)
555
556
557 return;
558 assertEquals(
559 "1-master-r\n2\n3-side-r",
560 contentAsString(db, merger.getResultTreeId(), "f"));
561 if (indexState != IndexState.Bare)
562 assertEquals(
563 "[f, mode:100644, content:1-master-r\n2\n3-side-r\n][m.c, mode:100644, content:0][m.m, mode:100644, content:1][s.c, mode:100644, content:0][s.m, mode:100644, content:1]",
564 indexState(RepositoryTestCase.CONTENT));
565 if (worktreeState != WorktreeState.Bare
566 && worktreeState != WorktreeState.Missing) {
567 assertEquals(
568 "1-master-r\n2\n3-side-r\n",
569 read("f"));
570 assertTrue(check("s.c"));
571 assertFalse(check("s.d"));
572 assertTrue(check("s.m"));
573 assertTrue(check("m.c"));
574 assertFalse(check("m.d"));
575 assertTrue(check("m.m"));
576 }
577 } catch (NoMergeBaseException e) {
578 assertEquals(MergeStrategy.RESOLVE, strategy);
579 assertEquals(e.getReason(),
580 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
581 }
582 }
583
584 @Theory
585
586
587
588
589
590
591
592
593
594
595
596
597
598 public void crissCrossMerge_nonmergeable(MergeStrategy strategy,
599 IndexState indexState, WorktreeState worktreeState)
600 throws Exception {
601 if (!validateStates(indexState, worktreeState))
602 return;
603
604 BranchBuilder master = db_t.branch("master");
605 RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
606 .message("m0").create();
607 RevCommit m1 = master.commit()
608 .add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m1")
609 .create();
610 db_t.getRevWalk().parseCommit(m1);
611
612 BranchBuilder side = db_t.branch("side");
613 RevCommit s1 = side.commit().parent(m0)
614 .add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1")
615 .create();
616 RevCommit s2 = side.commit().parent(m1)
617 .add("f", "1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n")
618 .message("s2(merge)").create();
619 RevCommit m2 = master.commit().parent(s1)
620 .add("f", "1-master\n2\n3\n4\n5\n6\n7-conflict\n8\n9-side\n")
621 .message("m2(merge)").create();
622
623 Git git = Git.wrap(db);
624 git.checkout().setName("master").call();
625 modifyWorktree(worktreeState, "f", "side");
626 modifyIndex(indexState, "f", "side");
627
628 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
629 worktreeState == WorktreeState.Bare);
630 if (worktreeState != WorktreeState.Bare)
631 merger.setWorkingTreeIterator(new FileTreeIterator(db));
632 try {
633 assertFalse(merger.merge(new RevCommit[] { m2, s2 }));
634 assertEquals(MergeStrategy.RECURSIVE, strategy);
635 if (indexState == IndexState.SameAsHead
636 && worktreeState == WorktreeState.SameAsHead) {
637 assertEquals(
638 "[f, mode:100644, stage:1, content:1-master\n2\n3\n4\n5\n6\n7\n8\n9-side\n]"
639 + "[f, mode:100644, stage:2, content:1-master\n2\n3\n4\n5\n6\n7-conflict\n8\n9-side\n]"
640 + "[f, mode:100644, stage:3, content:1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n]",
641 indexState(RepositoryTestCase.CONTENT));
642 assertEquals(
643 "1-master\n2\n3\n4\n5\n6\n<<<<<<< OURS\n7-conflict\n=======\n7-res(side)\n>>>>>>> THEIRS\n8\n9-side\n",
644 read("f"));
645 }
646 } catch (NoMergeBaseException e) {
647 assertEquals(MergeStrategy.RESOLVE, strategy);
648 assertEquals(e.getReason(),
649 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
650 }
651 }
652
653 @Theory
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670 public void crissCrossMerge_ThreeCommonPredecessors(MergeStrategy strategy,
671 IndexState indexState, WorktreeState worktreeState)
672 throws Exception {
673 if (!validateStates(indexState, worktreeState))
674 return;
675
676 BranchBuilder master = db_t.branch("master");
677 RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
678 .message("m0").create();
679 RevCommit m1 = master.commit()
680 .add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m1")
681 .create();
682 BranchBuilder side = db_t.branch("side");
683 RevCommit s1 = side.commit().parent(m0)
684 .add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1")
685 .create();
686 BranchBuilder other = db_t.branch("other");
687 RevCommit o1 = other.commit().parent(m0)
688 .add("f", "1\n2\n3\n4\n5-other\n6\n7\n8\n9\n").message("o1")
689 .create();
690
691 RevCommit m2 = master
692 .commit()
693 .parent(s1)
694 .parent(o1)
695 .add("f",
696 "1-master\n2\n3-res(master)\n4\n5-other\n6\n7\n8\n9-side\n")
697 .message("m2(merge)").create();
698
699 RevCommit s2 = side
700 .commit()
701 .parent(m1)
702 .parent(o1)
703 .add("f",
704 "1-master\n2\n3\n4\n5-other\n6\n7-res(side)\n8\n9-side\n")
705 .message("s2(merge)").create();
706
707 Git git = Git.wrap(db);
708 git.checkout().setName("master").call();
709 modifyWorktree(worktreeState, "f", "side");
710 modifyIndex(indexState, "f", "side");
711
712 ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
713 worktreeState == WorktreeState.Bare);
714 if (worktreeState != WorktreeState.Bare)
715 merger.setWorkingTreeIterator(new FileTreeIterator(db));
716 try {
717 boolean expectSuccess = true;
718 if (!(indexState == IndexState.Bare
719 || indexState == IndexState.Missing || indexState == IndexState.SameAsHead))
720
721 expectSuccess = false;
722 else if (worktreeState == WorktreeState.DifferentFromHeadAndOther
723 || worktreeState == WorktreeState.SameAsOther)
724
725 expectSuccess = false;
726
727 assertEquals(Boolean.valueOf(expectSuccess),
728 Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
729 assertEquals(MergeStrategy.RECURSIVE, strategy);
730 if (!expectSuccess)
731
732 return;
733 assertEquals(
734 "1-master\n2\n3-res(master)\n4\n5-other\n6\n7-res(side)\n8\n9-side",
735 contentAsString(db, merger.getResultTreeId(), "f"));
736 if (indexState != IndexState.Bare)
737 assertEquals(
738 "[f, mode:100644, content:1-master\n2\n3-res(master)\n4\n5-other\n6\n7-res(side)\n8\n9-side\n]",
739 indexState(RepositoryTestCase.CONTENT));
740 if (worktreeState != WorktreeState.Bare
741 && worktreeState != WorktreeState.Missing)
742 assertEquals(
743 "1-master\n2\n3-res(master)\n4\n5-other\n6\n7-res(side)\n8\n9-side\n",
744 read("f"));
745 } catch (NoMergeBaseException e) {
746 assertEquals(MergeStrategy.RESOLVE, strategy);
747 assertEquals(e.getReason(),
748 MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
749 }
750 }
751
752 void modifyIndex(IndexState indexState, String path, String other)
753 throws Exception {
754 RevBlob blob;
755 switch (indexState) {
756 case Missing:
757 setIndex(null, path);
758 break;
759 case SameAsHead:
760 setIndex(contentId(Constants.HEAD, path), path);
761 break;
762 case SameAsOther:
763 setIndex(contentId(other, path), path);
764 break;
765 case SameAsWorkTree:
766 blob = db_t.blob(read(path));
767 setIndex(blob, path);
768 break;
769 case DifferentFromHeadAndOtherAndWorktree:
770 blob = db_t.blob(Integer.toString(counter++));
771 setIndex(blob, path);
772 break;
773 case Bare:
774 File file = new File(db.getDirectory(), "index");
775 if (!file.exists())
776 return;
777 db.close();
778 file.delete();
779 db = new FileRepository(db.getDirectory());
780 db_t = new TestRepository<FileRepository>(db);
781 break;
782 }
783 }
784
785 private void setIndex(final ObjectId id, String path)
786 throws MissingObjectException, IOException {
787 DirCache lockedDircache;
788 DirCacheEditor dcedit;
789
790 lockedDircache = db.lockDirCache();
791 dcedit = lockedDircache.editor();
792 try {
793 if (id != null) {
794 final ObjectLoader contLoader = db.newObjectReader().open(id);
795 dcedit.add(new DirCacheEditor.PathEdit(path) {
796 @Override
797 public void apply(DirCacheEntry ent) {
798 ent.setFileMode(FileMode.REGULAR_FILE);
799 ent.setLength(contLoader.getSize());
800 ent.setObjectId(id);
801 }
802 });
803 } else
804 dcedit.add(new DirCacheEditor.DeletePath(path));
805 } finally {
806 dcedit.commit();
807 }
808 }
809
810 private ObjectId contentId(String revName, String path) throws Exception {
811 RevCommit headCommit = db_t.getRevWalk().parseCommit(
812 db.resolve(revName));
813 db_t.parseBody(headCommit);
814 return db_t.get(headCommit.getTree(), path).getId();
815 }
816
817 void modifyWorktree(WorktreeState worktreeState, String path, String other)
818 throws Exception {
819 FileOutputStream fos = null;
820 ObjectId bloblId;
821
822 try {
823 switch (worktreeState) {
824 case Missing:
825 new File(db.getWorkTree(), path).delete();
826 break;
827 case DifferentFromHeadAndOther:
828 write(new File(db.getWorkTree(), path),
829 Integer.toString(counter++));
830 break;
831 case SameAsHead:
832 bloblId = contentId(Constants.HEAD, path);
833 fos = new FileOutputStream(new File(db.getWorkTree(), path));
834 db.newObjectReader().open(bloblId).copyTo(fos);
835 break;
836 case SameAsOther:
837 bloblId = contentId(other, path);
838 fos = new FileOutputStream(new File(db.getWorkTree(), path));
839 db.newObjectReader().open(bloblId).copyTo(fos);
840 break;
841 case Bare:
842 if (db.isBare())
843 return;
844 File workTreeFile = db.getWorkTree();
845 db.getConfig().setBoolean("core", null, "bare", true);
846 db.getDirectory().renameTo(new File(workTreeFile, "test.git"));
847 db = new FileRepository(new File(workTreeFile, "test.git"));
848 db_t = new TestRepository<FileRepository>(db);
849 }
850 } finally {
851 if (fos != null)
852 fos.close();
853 }
854 }
855
856 private boolean validateStates(IndexState indexState,
857 WorktreeState worktreeState) {
858 if (worktreeState == WorktreeState.Bare
859 && indexState != IndexState.Bare)
860 return false;
861 if (worktreeState != WorktreeState.Bare
862 && indexState == IndexState.Bare)
863 return false;
864 if (worktreeState != WorktreeState.DifferentFromHeadAndOther
865 && indexState == IndexState.SameAsWorkTree)
866
867
868 return false;
869 return true;
870 }
871
872 private String contentAsString(Repository r, ObjectId treeId, String path)
873 throws MissingObjectException, IOException {
874 TreeWalk tw = new TreeWalk(r);
875 tw.addTree(treeId);
876 tw.setFilter(PathFilter.create(path));
877 tw.setRecursive(true);
878 if (!tw.next())
879 return null;
880 AnyObjectId blobId = tw.getObjectId(0);
881
882 StringBuilder result = new StringBuilder();
883 BufferedReader br = null;
884 ObjectReader or = r.newObjectReader();
885 try {
886 br = new BufferedReader(new InputStreamReader(or.open(blobId)
887 .openStream()));
888 String line;
889 boolean first = true;
890 while ((line = br.readLine()) != null) {
891 if (!first)
892 result.append('\n');
893 result.append(line);
894 first = false;
895 }
896 return result.toString();
897 } finally {
898 if (br != null)
899 br.close();
900 }
901 }
902 }