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