@@ -1395,6 +1395,15 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
1395
1395
return ret ;
1396
1396
}
1397
1397
1398
+ static int tree_has_path (struct tree * tree , const char * path )
1399
+ {
1400
+ struct object_id hashy ;
1401
+ unsigned int mode_o ;
1402
+
1403
+ return !get_tree_entry (& tree -> object .oid , path ,
1404
+ & hashy , & mode_o );
1405
+ }
1406
+
1398
1407
static void get_renamed_dir_portion (const char * old_path , const char * new_path ,
1399
1408
char * * old_dir , char * * new_dir )
1400
1409
{
@@ -1450,6 +1459,112 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
1450
1459
}
1451
1460
}
1452
1461
1462
+ static void remove_hashmap_entries (struct hashmap * dir_renames ,
1463
+ struct string_list * items_to_remove )
1464
+ {
1465
+ int i ;
1466
+ struct dir_rename_entry * entry ;
1467
+
1468
+ for (i = 0 ; i < items_to_remove -> nr ; i ++ ) {
1469
+ entry = items_to_remove -> items [i ].util ;
1470
+ hashmap_remove (dir_renames , entry , NULL );
1471
+ }
1472
+ string_list_clear (items_to_remove , 0 );
1473
+ }
1474
+
1475
+ /*
1476
+ * There are a couple things we want to do at the directory level:
1477
+ * 1. Check for both sides renaming to the same thing, in order to avoid
1478
+ * implicit renaming of files that should be left in place. (See
1479
+ * testcase 6b in t6043 for details.)
1480
+ * 2. Prune directory renames if there are still files left in the
1481
+ * the original directory. These represent a partial directory rename,
1482
+ * i.e. a rename where only some of the files within the directory
1483
+ * were renamed elsewhere. (Technically, this could be done earlier
1484
+ * in get_directory_renames(), except that would prevent us from
1485
+ * doing the previous check and thus failing testcase 6b.)
1486
+ * 3. Check for rename/rename(1to2) conflicts (at the directory level).
1487
+ * In the future, we could potentially record this info as well and
1488
+ * omit reporting rename/rename(1to2) conflicts for each path within
1489
+ * the affected directories, thus cleaning up the merge output.
1490
+ * NOTE: We do NOT check for rename/rename(2to1) conflicts at the
1491
+ * directory level, because merging directories is fine. If it
1492
+ * causes conflicts for files within those merged directories, then
1493
+ * that should be detected at the individual path level.
1494
+ */
1495
+ static void handle_directory_level_conflicts (struct merge_options * o ,
1496
+ struct hashmap * dir_re_head ,
1497
+ struct tree * head ,
1498
+ struct hashmap * dir_re_merge ,
1499
+ struct tree * merge )
1500
+ {
1501
+ struct hashmap_iter iter ;
1502
+ struct dir_rename_entry * head_ent ;
1503
+ struct dir_rename_entry * merge_ent ;
1504
+
1505
+ struct string_list remove_from_head = STRING_LIST_INIT_NODUP ;
1506
+ struct string_list remove_from_merge = STRING_LIST_INIT_NODUP ;
1507
+
1508
+ hashmap_iter_init (dir_re_head , & iter );
1509
+ while ((head_ent = hashmap_iter_next (& iter ))) {
1510
+ merge_ent = dir_rename_find_entry (dir_re_merge , head_ent -> dir );
1511
+ if (merge_ent &&
1512
+ !head_ent -> non_unique_new_dir &&
1513
+ !merge_ent -> non_unique_new_dir &&
1514
+ !strbuf_cmp (& head_ent -> new_dir , & merge_ent -> new_dir )) {
1515
+ /* 1. Renamed identically; remove it from both sides */
1516
+ string_list_append (& remove_from_head ,
1517
+ head_ent -> dir )-> util = head_ent ;
1518
+ strbuf_release (& head_ent -> new_dir );
1519
+ string_list_append (& remove_from_merge ,
1520
+ merge_ent -> dir )-> util = merge_ent ;
1521
+ strbuf_release (& merge_ent -> new_dir );
1522
+ } else if (tree_has_path (head , head_ent -> dir )) {
1523
+ /* 2. This wasn't a directory rename after all */
1524
+ string_list_append (& remove_from_head ,
1525
+ head_ent -> dir )-> util = head_ent ;
1526
+ strbuf_release (& head_ent -> new_dir );
1527
+ }
1528
+ }
1529
+
1530
+ remove_hashmap_entries (dir_re_head , & remove_from_head );
1531
+ remove_hashmap_entries (dir_re_merge , & remove_from_merge );
1532
+
1533
+ hashmap_iter_init (dir_re_merge , & iter );
1534
+ while ((merge_ent = hashmap_iter_next (& iter ))) {
1535
+ head_ent = dir_rename_find_entry (dir_re_head , merge_ent -> dir );
1536
+ if (tree_has_path (merge , merge_ent -> dir )) {
1537
+ /* 2. This wasn't a directory rename after all */
1538
+ string_list_append (& remove_from_merge ,
1539
+ merge_ent -> dir )-> util = merge_ent ;
1540
+ } else if (head_ent &&
1541
+ !head_ent -> non_unique_new_dir &&
1542
+ !merge_ent -> non_unique_new_dir ) {
1543
+ /* 3. rename/rename(1to2) */
1544
+ /*
1545
+ * We can assume it's not rename/rename(1to1) because
1546
+ * that was case (1), already checked above. So we
1547
+ * know that head_ent->new_dir and merge_ent->new_dir
1548
+ * are different strings.
1549
+ */
1550
+ output (o , 1 , _ ("CONFLICT (rename/rename): "
1551
+ "Rename directory %s->%s in %s. "
1552
+ "Rename directory %s->%s in %s" ),
1553
+ head_ent -> dir , head_ent -> new_dir .buf , o -> branch1 ,
1554
+ head_ent -> dir , merge_ent -> new_dir .buf , o -> branch2 );
1555
+ string_list_append (& remove_from_head ,
1556
+ head_ent -> dir )-> util = head_ent ;
1557
+ strbuf_release (& head_ent -> new_dir );
1558
+ string_list_append (& remove_from_merge ,
1559
+ merge_ent -> dir )-> util = merge_ent ;
1560
+ strbuf_release (& merge_ent -> new_dir );
1561
+ }
1562
+ }
1563
+
1564
+ remove_hashmap_entries (dir_re_head , & remove_from_head );
1565
+ remove_hashmap_entries (dir_re_merge , & remove_from_merge );
1566
+ }
1567
+
1453
1568
static struct hashmap * get_directory_renames (struct diff_queue_struct * pairs ,
1454
1569
struct tree * tree )
1455
1570
{
@@ -1911,6 +2026,10 @@ static int handle_renames(struct merge_options *o,
1911
2026
dir_re_head = get_directory_renames (head_pairs , head );
1912
2027
dir_re_merge = get_directory_renames (merge_pairs , merge );
1913
2028
2029
+ handle_directory_level_conflicts (o ,
2030
+ dir_re_head , head ,
2031
+ dir_re_merge , merge );
2032
+
1914
2033
ri -> head_renames = get_renames (o , head_pairs , head ,
1915
2034
common , head , merge , entries );
1916
2035
ri -> merge_renames = get_renames (o , merge_pairs , merge ,
0 commit comments