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