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