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