@@ -1384,6 +1384,270 @@ public void checkMergeConflictInVirtualAncestor(
1384
1384
git .merge ().include (commitB ).call ();
1385
1385
}
1386
1386
1387
+ /**
1388
+ * Merging two commits with a file/dir conflict in the virtual ancestor.
1389
+ *
1390
+ * <p>
1391
+ * Those conflicts should be ignored, otherwise the found base can not be used by the
1392
+ * RecursiveMerger.
1393
+ * <pre>
1394
+ * --------------
1395
+ * | \
1396
+ * | C1 - C4 --- ? master
1397
+ * | / /
1398
+ * | I - A1 - C2 - C3 second-branch
1399
+ * | \ /
1400
+ * \ \ /
1401
+ * ----A2-------- branch-to-merge
1402
+ * </pre>
1403
+ * <p>
1404
+ * <p>
1405
+ * Path "a" is initially a file in I and A1. It is changed to a directory in A2
1406
+ * ("branch-to-merge").
1407
+ * <p>
1408
+ * A2 is merged into "master" and "second-branch". The dir/file merge conflict is resolved
1409
+ * manually, results in C4 and C3.
1410
+ * <p>
1411
+ * While merging C3 and C4, A1 and A2 are the base commits found by the recursive merge that
1412
+ * have the dir/file conflict.
1413
+ */
1414
+ @ Theory
1415
+ public void checkFileDirMergeConflictInVirtualAncestor_NoConflictInChildren (
1416
+ MergeStrategy strategy )
1417
+ throws Exception {
1418
+ if (!strategy .equals (MergeStrategy .RECURSIVE )) {
1419
+ return ;
1420
+ }
1421
+
1422
+ Git git = Git .wrap (db );
1423
+
1424
+ // master
1425
+ writeTrashFile ("a" , "initial content" );
1426
+ git .add ().addFilepattern ("a" ).call ();
1427
+ RevCommit commitI = git .commit ().setMessage ("Initial commit" ).call ();
1428
+
1429
+ writeTrashFile ("a" , "content in Ancestor 1" );
1430
+ git .add ().addFilepattern ("a" ).call ();
1431
+ RevCommit commitA1 = git .commit ().setMessage ("Ancestor 1" ).call ();
1432
+
1433
+ writeTrashFile ("a" , "content in Child 1 (commited on master)" );
1434
+ git .add ().addFilepattern ("a" ).call ();
1435
+ // commit C1M
1436
+ git .commit ().setMessage ("Child 1 on master" ).call ();
1437
+
1438
+ git .checkout ().setCreateBranch (true ).setStartPoint (commitI ).setName ("branch-to-merge" ).call ();
1439
+ // "a" becomes a directory in A2
1440
+ git .rm ().addFilepattern ("a" ).call ();
1441
+ writeTrashFile ("a/content" , "content in Ancestor 2 (commited on branch-to-merge)" );
1442
+ git .add ().addFilepattern ("a/content" ).call ();
1443
+ RevCommit commitA2 = git .commit ().setMessage ("Ancestor 2" ).call ();
1444
+
1445
+ // second branch
1446
+ git .checkout ().setCreateBranch (true ).setStartPoint (commitA1 ).setName ("second-branch" ).call ();
1447
+ writeTrashFile ("a" , "content in Child 2 (commited on second-branch)" );
1448
+ git .add ().addFilepattern ("a" ).call ();
1449
+ // commit C2S
1450
+ git .commit ().setMessage ("Child 2 on second-branch" ).call ();
1451
+
1452
+ // Merge branch-to-merge into second-branch
1453
+ MergeResult mergeResult = git .merge ().include (commitA2 ).setStrategy (strategy ).call ();
1454
+ assertEquals (mergeResult .getNewHead (), null );
1455
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1456
+ // Resolve the conflict manually, merge "a" as a file
1457
+ git .rm ().addFilepattern ("a" ).call ();
1458
+ git .rm ().addFilepattern ("a/content" ).call ();
1459
+ writeTrashFile ("a" , "merge conflict resolution" );
1460
+ git .add ().addFilepattern ("a" ).call ();
1461
+ RevCommit commitC3S = git .commit ().setMessage ("Child 3 on second bug - resolve merge conflict" )
1462
+ .call ();
1463
+
1464
+ // Merge branch-to-merge into master
1465
+ git .checkout ().setName ("master" ).call ();
1466
+ mergeResult = git .merge ().include (commitA2 ).setStrategy (strategy ).call ();
1467
+ assertEquals (mergeResult .getNewHead (), null );
1468
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1469
+
1470
+ // Resolve the conflict manually - merge "a" as a file
1471
+ git .rm ().addFilepattern ("a" ).call ();
1472
+ git .rm ().addFilepattern ("a/content" ).call ();
1473
+ writeTrashFile ("a" , "merge conflict resolution" );
1474
+ git .add ().addFilepattern ("a" ).call ();
1475
+ // commit C4M
1476
+ git .commit ().setMessage ("Child 4 on master - resolve merge conflict" ).call ();
1477
+
1478
+ // Merge C4M (second-branch) into master (C3S)
1479
+ // Conflict in virtual base should be here, but there are no conflicts in
1480
+ // children
1481
+ mergeResult = git .merge ().include (commitC3S ).call ();
1482
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .MERGED );
1483
+
1484
+ }
1485
+
1486
+ @ Theory
1487
+ public void checkFileDirMergeConflictInVirtualAncestor_ConflictInChildren_FileDir (MergeStrategy strategy )
1488
+ throws Exception {
1489
+ if (!strategy .equals (MergeStrategy .RECURSIVE )) {
1490
+ return ;
1491
+ }
1492
+
1493
+ Git git = Git .wrap (db );
1494
+
1495
+ // master
1496
+ writeTrashFile ("a" , "initial content" );
1497
+ git .add ().addFilepattern ("a" ).call ();
1498
+ RevCommit commitI = git .commit ().setMessage ("Initial commit" ).call ();
1499
+
1500
+ writeTrashFile ("a" , "content in Ancestor 1" );
1501
+ git .add ().addFilepattern ("a" ).call ();
1502
+ RevCommit commitA1 = git .commit ().setMessage ("Ancestor 1" ).call ();
1503
+
1504
+ writeTrashFile ("a" , "content in Child 1 (commited on master)" );
1505
+ git .add ().addFilepattern ("a" ).call ();
1506
+ // commit C1M
1507
+ git .commit ().setMessage ("Child 1 on master" ).call ();
1508
+
1509
+ git .checkout ().setCreateBranch (true ).setStartPoint (commitI ).setName ("branch-to-merge" ).call ();
1510
+
1511
+ // "a" becomes a directory in A2
1512
+ git .rm ().addFilepattern ("a" ).call ();
1513
+ writeTrashFile ("a/content" , "content in Ancestor 2 (commited on branch-to-merge)" );
1514
+ git .add ().addFilepattern ("a/content" ).call ();
1515
+ RevCommit commitA2 = git .commit ().setMessage ("Ancestor 2" ).call ();
1516
+
1517
+ // second branch
1518
+ git .checkout ().setCreateBranch (true ).setStartPoint (commitA1 ).setName ("second-branch" ).call ();
1519
+ writeTrashFile ("a" , "content in Child 2 (commited on second-branch)" );
1520
+ git .add ().addFilepattern ("a" ).call ();
1521
+ // commit C2S
1522
+ git .commit ().setMessage ("Child 2 on second-branch" ).call ();
1523
+
1524
+ // Merge branch-to-merge into second-branch
1525
+ MergeResult mergeResult = git .merge ().include (commitA2 ).setStrategy (strategy ).call ();
1526
+ assertEquals (mergeResult .getNewHead (), null );
1527
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1528
+ // Resolve the conflict manually - write a file
1529
+ git .rm ().addFilepattern ("a" ).call ();
1530
+ git .rm ().addFilepattern ("a/content" ).call ();
1531
+ writeTrashFile ("a" ,
1532
+ "content in Child 3 (commited on second-branch) - merge conflict resolution" );
1533
+ git .add ().addFilepattern ("a" ).call ();
1534
+ RevCommit commitC3S = git .commit ().setMessage ("Child 3 on second bug - resolve merge conflict" )
1535
+ .call ();
1536
+
1537
+ // Merge branch-to-merge into master
1538
+ git .checkout ().setName ("master" ).call ();
1539
+ mergeResult = git .merge ().include (commitA2 ).setStrategy (strategy ).call ();
1540
+ assertEquals (mergeResult .getNewHead (), null );
1541
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1542
+
1543
+ // Resolve the conflict manually - write a file
1544
+ git .rm ().addFilepattern ("a" ).call ();
1545
+ git .rm ().addFilepattern ("a/content" ).call ();
1546
+ writeTrashFile ("a" , "content in Child 4 (commited on master) - merge conflict resolution" );
1547
+ git .add ().addFilepattern ("a" ).call ();
1548
+ // commit C4M
1549
+ git .commit ().setMessage ("Child 4 on master - resolve merge conflict" ).call ();
1550
+
1551
+ // Merge C4M (second-branch) into master (C3S)
1552
+ // Conflict in virtual base should be here
1553
+ mergeResult = git .merge ().include (commitC3S ).call ();
1554
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1555
+ String expected =
1556
+ "<<<<<<< HEAD\n " + "content in Child 4 (commited on master) - merge conflict resolution\n "
1557
+ + "=======\n "
1558
+ + "content in Child 3 (commited on second-branch) - merge conflict resolution\n "
1559
+ + ">>>>>>> " + commitC3S .name () + "\n " ;
1560
+ assertEquals (expected , read ("a" ));
1561
+ // Nothing was populated from the ancestors.
1562
+ assertEquals (
1563
+ "[a, mode:100644, stage:2, content:content in Child 4 (commited on master) - merge conflict resolution][a, mode:100644, stage:3, content:content in Child 3 (commited on second-branch) - merge conflict resolution]" ,
1564
+ indexState (CONTENT ));
1565
+ }
1566
+
1567
+ /**
1568
+ * Same test as above, but "a" is a dir in A1 and a file in A2
1569
+ */
1570
+ @ Theory
1571
+ public void checkFileDirMergeConflictInVirtualAncestor_ConflictInChildren_DirFile (MergeStrategy strategy )
1572
+ throws Exception {
1573
+ if (!strategy .equals (MergeStrategy .RECURSIVE )) {
1574
+ return ;
1575
+ }
1576
+
1577
+ Git git = Git .wrap (db );
1578
+
1579
+ // master
1580
+ writeTrashFile ("a/content" , "initial content" );
1581
+ git .add ().addFilepattern ("a/content" ).call ();
1582
+ RevCommit commitI = git .commit ().setMessage ("Initial commit" ).call ();
1583
+
1584
+ writeTrashFile ("a/content" , "content in Ancestor 1" );
1585
+ git .add ().addFilepattern ("a/content" ).call ();
1586
+ RevCommit commitA1 = git .commit ().setMessage ("Ancestor 1" ).call ();
1587
+
1588
+ writeTrashFile ("a/content" , "content in Child 1 (commited on master)" );
1589
+ git .add ().addFilepattern ("a/content" ).call ();
1590
+ // commit C1M
1591
+ git .commit ().setMessage ("Child 1 on master" ).call ();
1592
+
1593
+ git .checkout ().setCreateBranch (true ).setStartPoint (commitI ).setName ("branch-to-merge" ).call ();
1594
+
1595
+ // "a" becomes a file in A2
1596
+ git .rm ().addFilepattern ("a/content" ).call ();
1597
+ writeTrashFile ("a" , "content in Ancestor 2 (commited on branch-to-merge)" );
1598
+ git .add ().addFilepattern ("a" ).call ();
1599
+ RevCommit commitA2 = git .commit ().setMessage ("Ancestor 2" ).call ();
1600
+
1601
+ // second branch
1602
+ git .checkout ().setCreateBranch (true ).setStartPoint (commitA1 ).setName ("second-branch" ).call ();
1603
+ writeTrashFile ("a/content" , "content in Child 2 (commited on second-branch)" );
1604
+ git .add ().addFilepattern ("a/content" ).call ();
1605
+ // commit C2S
1606
+ git .commit ().setMessage ("Child 2 on second-branch" ).call ();
1607
+
1608
+ // Merge branch-to-merge into second-branch
1609
+ MergeResult mergeResult = git .merge ().include (commitA2 ).setStrategy (strategy ).call ();
1610
+ assertEquals (mergeResult .getNewHead (), null );
1611
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1612
+ // Resolve the conflict manually - write a file
1613
+ git .rm ().addFilepattern ("a" ).call ();
1614
+ git .rm ().addFilepattern ("a/content" ).call ();
1615
+ deleteTrashFile ("a/content" );
1616
+ deleteTrashFile ("a" );
1617
+ writeTrashFile ("a" , "content in Child 3 (commited on second-branch) - merge conflict resolution" );
1618
+ git .add ().addFilepattern ("a" ).call ();
1619
+ RevCommit commitC3S = git .commit ().setMessage ("Child 3 on second bug - resolve merge conflict" ).call ();
1620
+
1621
+ // Merge branch-to-merge into master
1622
+ git .checkout ().setName ("master" ).call ();
1623
+ mergeResult = git .merge ().include (commitA2 ).setStrategy (strategy ).call ();
1624
+ assertEquals (mergeResult .getNewHead (), null );
1625
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1626
+
1627
+ // Resolve the conflict manually - write a file
1628
+ git .rm ().addFilepattern ("a" ).call ();
1629
+ git .rm ().addFilepattern ("a/content" ).call ();
1630
+ deleteTrashFile ("a/content" );
1631
+ deleteTrashFile ("a" );
1632
+ writeTrashFile ("a" , "content in Child 4 (commited on master) - merge conflict resolution" );
1633
+ git .add ().addFilepattern ("a" ).call ();
1634
+ // commit C4M
1635
+ git .commit ().setMessage ("Child 4 on master - resolve merge conflict" ).call ();
1636
+
1637
+ // Merge C4M (second-branch) into master (C3S)
1638
+ // Conflict in virtual base should be here
1639
+ mergeResult = git .merge ().include (commitC3S ).call ();
1640
+ assertEquals (mergeResult .getMergeStatus (), MergeStatus .CONFLICTING );
1641
+ String expected = "<<<<<<< HEAD\n " + "content in Child 4 (commited on master) - merge conflict resolution\n "
1642
+ + "=======\n " + "content in Child 3 (commited on second-branch) - merge conflict resolution\n "
1643
+ + ">>>>>>> " + commitC3S .name () + "\n " ;
1644
+ assertEquals (expected , read ("a" ));
1645
+ // Nothing was populated from the ancestors.
1646
+ assertEquals (
1647
+ "[a, mode:100644, stage:2, content:content in Child 4 (commited on master) - merge conflict resolution][a, mode:100644, stage:3, content:content in Child 3 (commited on second-branch) - merge conflict resolution]" ,
1648
+ indexState (CONTENT ));
1649
+ }
1650
+
1387
1651
private void writeSubmodule (String path , ObjectId commit )
1388
1652
throws IOException , ConfigInvalidException {
1389
1653
addSubmoduleToIndex (path , commit );
0 commit comments