@@ -49,6 +49,44 @@ static unsigned int path_hash(const char *path)
49
49
return ignore_case ? strihash (path ) : strhash (path );
50
50
}
51
51
52
+ static struct dir_rename_entry * dir_rename_find_entry (struct hashmap * hashmap ,
53
+ char * dir )
54
+ {
55
+ struct dir_rename_entry key ;
56
+
57
+ if (dir == NULL )
58
+ return NULL ;
59
+ hashmap_entry_init (& key , strhash (dir ));
60
+ key .dir = dir ;
61
+ return hashmap_get (hashmap , & key , NULL );
62
+ }
63
+
64
+ static int dir_rename_cmp (const void * unused_cmp_data ,
65
+ const void * entry ,
66
+ const void * entry_or_key ,
67
+ const void * unused_keydata )
68
+ {
69
+ const struct dir_rename_entry * e1 = entry ;
70
+ const struct dir_rename_entry * e2 = entry_or_key ;
71
+
72
+ return strcmp (e1 -> dir , e2 -> dir );
73
+ }
74
+
75
+ static void dir_rename_init (struct hashmap * map )
76
+ {
77
+ hashmap_init (map , dir_rename_cmp , NULL , 0 );
78
+ }
79
+
80
+ static void dir_rename_entry_init (struct dir_rename_entry * entry ,
81
+ char * directory )
82
+ {
83
+ hashmap_entry_init (entry , strhash (directory ));
84
+ entry -> dir = directory ;
85
+ entry -> non_unique_new_dir = 0 ;
86
+ strbuf_init (& entry -> new_dir , 0 );
87
+ string_list_init (& entry -> possible_new_dirs , 0 );
88
+ }
89
+
52
90
static void flush_output (struct merge_options * o )
53
91
{
54
92
if (o -> buffer_output < 2 && o -> obuf .len ) {
@@ -1357,6 +1395,169 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
1357
1395
return ret ;
1358
1396
}
1359
1397
1398
+ static void get_renamed_dir_portion (const char * old_path , const char * new_path ,
1399
+ char * * old_dir , char * * new_dir )
1400
+ {
1401
+ char * end_of_old , * end_of_new ;
1402
+ int old_len , new_len ;
1403
+
1404
+ * old_dir = NULL ;
1405
+ * new_dir = NULL ;
1406
+
1407
+ /*
1408
+ * For
1409
+ * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
1410
+ * the "e/foo.c" part is the same, we just want to know that
1411
+ * "a/b/c/d" was renamed to "a/b/some/thing/else"
1412
+ * so, for this example, this function returns "a/b/c/d" in
1413
+ * *old_dir and "a/b/some/thing/else" in *new_dir.
1414
+ *
1415
+ * Also, if the basename of the file changed, we don't care. We
1416
+ * want to know which portion of the directory, if any, changed.
1417
+ */
1418
+ end_of_old = strrchr (old_path , '/' );
1419
+ end_of_new = strrchr (new_path , '/' );
1420
+
1421
+ if (end_of_old == NULL || end_of_new == NULL )
1422
+ return ;
1423
+ while (* -- end_of_new == * -- end_of_old &&
1424
+ end_of_old != old_path &&
1425
+ end_of_new != new_path )
1426
+ ; /* Do nothing; all in the while loop */
1427
+ /*
1428
+ * We've found the first non-matching character in the directory
1429
+ * paths. That means the current directory we were comparing
1430
+ * represents the rename. Move end_of_old and end_of_new back
1431
+ * to the full directory name.
1432
+ */
1433
+ if (* end_of_old == '/' )
1434
+ end_of_old ++ ;
1435
+ if (* end_of_old != '/' )
1436
+ end_of_new ++ ;
1437
+ end_of_old = strchr (end_of_old , '/' );
1438
+ end_of_new = strchr (end_of_new , '/' );
1439
+
1440
+ /*
1441
+ * It may have been the case that old_path and new_path were the same
1442
+ * directory all along. Don't claim a rename if they're the same.
1443
+ */
1444
+ old_len = end_of_old - old_path ;
1445
+ new_len = end_of_new - new_path ;
1446
+
1447
+ if (old_len != new_len || strncmp (old_path , new_path , old_len )) {
1448
+ * old_dir = xstrndup (old_path , old_len );
1449
+ * new_dir = xstrndup (new_path , new_len );
1450
+ }
1451
+ }
1452
+
1453
+ static struct hashmap * get_directory_renames (struct diff_queue_struct * pairs ,
1454
+ struct tree * tree )
1455
+ {
1456
+ struct hashmap * dir_renames ;
1457
+ struct hashmap_iter iter ;
1458
+ struct dir_rename_entry * entry ;
1459
+ int i ;
1460
+
1461
+ /*
1462
+ * Typically, we think of a directory rename as all files from a
1463
+ * certain directory being moved to a target directory. However,
1464
+ * what if someone first moved two files from the original
1465
+ * directory in one commit, and then renamed the directory
1466
+ * somewhere else in a later commit? At merge time, we just know
1467
+ * that files from the original directory went to two different
1468
+ * places, and that the bulk of them ended up in the same place.
1469
+ * We want each directory rename to represent where the bulk of the
1470
+ * files from that directory end up; this function exists to find
1471
+ * where the bulk of the files went.
1472
+ *
1473
+ * The first loop below simply iterates through the list of file
1474
+ * renames, finding out how often each directory rename pair
1475
+ * possibility occurs.
1476
+ */
1477
+ dir_renames = xmalloc (sizeof (* dir_renames ));
1478
+ dir_rename_init (dir_renames );
1479
+ for (i = 0 ; i < pairs -> nr ; ++ i ) {
1480
+ struct string_list_item * item ;
1481
+ int * count ;
1482
+ struct diff_filepair * pair = pairs -> queue [i ];
1483
+ char * old_dir , * new_dir ;
1484
+
1485
+ /* File not part of directory rename if it wasn't renamed */
1486
+ if (pair -> status != 'R' )
1487
+ continue ;
1488
+
1489
+ get_renamed_dir_portion (pair -> one -> path , pair -> two -> path ,
1490
+ & old_dir , & new_dir );
1491
+ if (!old_dir )
1492
+ /* Directory didn't change at all; ignore this one. */
1493
+ continue ;
1494
+
1495
+ entry = dir_rename_find_entry (dir_renames , old_dir );
1496
+ if (!entry ) {
1497
+ entry = xmalloc (sizeof (* entry ));
1498
+ dir_rename_entry_init (entry , old_dir );
1499
+ hashmap_put (dir_renames , entry );
1500
+ } else {
1501
+ free (old_dir );
1502
+ }
1503
+ item = string_list_lookup (& entry -> possible_new_dirs , new_dir );
1504
+ if (!item ) {
1505
+ item = string_list_insert (& entry -> possible_new_dirs ,
1506
+ new_dir );
1507
+ item -> util = xcalloc (1 , sizeof (int ));
1508
+ } else {
1509
+ free (new_dir );
1510
+ }
1511
+ count = item -> util ;
1512
+ * count += 1 ;
1513
+ }
1514
+
1515
+ /*
1516
+ * For each directory with files moved out of it, we find out which
1517
+ * target directory received the most files so we can declare it to
1518
+ * be the "winning" target location for the directory rename. This
1519
+ * winner gets recorded in new_dir. If there is no winner
1520
+ * (multiple target directories received the same number of files),
1521
+ * we set non_unique_new_dir. Once we've determined the winner (or
1522
+ * that there is no winner), we no longer need possible_new_dirs.
1523
+ */
1524
+ hashmap_iter_init (dir_renames , & iter );
1525
+ while ((entry = hashmap_iter_next (& iter ))) {
1526
+ int max = 0 ;
1527
+ int bad_max = 0 ;
1528
+ char * best = NULL ;
1529
+
1530
+ for (i = 0 ; i < entry -> possible_new_dirs .nr ; i ++ ) {
1531
+ int * count = entry -> possible_new_dirs .items [i ].util ;
1532
+
1533
+ if (* count == max )
1534
+ bad_max = max ;
1535
+ else if (* count > max ) {
1536
+ max = * count ;
1537
+ best = entry -> possible_new_dirs .items [i ].string ;
1538
+ }
1539
+ }
1540
+ if (bad_max == max )
1541
+ entry -> non_unique_new_dir = 1 ;
1542
+ else {
1543
+ assert (entry -> new_dir .len == 0 );
1544
+ strbuf_addstr (& entry -> new_dir , best );
1545
+ }
1546
+ /*
1547
+ * The relevant directory sub-portion of the original full
1548
+ * filepaths were xstrndup'ed before inserting into
1549
+ * possible_new_dirs, and instead of manually iterating the
1550
+ * list and free'ing each, just lie and tell
1551
+ * possible_new_dirs that it did the strdup'ing so that it
1552
+ * will free them for us.
1553
+ */
1554
+ entry -> possible_new_dirs .strdup_strings = 1 ;
1555
+ string_list_clear (& entry -> possible_new_dirs , 1 );
1556
+ }
1557
+
1558
+ return dir_renames ;
1559
+ }
1560
+
1360
1561
/*
1361
1562
* Get information of all renames which occurred in 'pairs', making use of
1362
1563
* any implicit directory renames inferred from the other side of history.
@@ -1668,8 +1869,21 @@ struct rename_info {
1668
1869
struct string_list * merge_renames ;
1669
1870
};
1670
1871
1671
- static void initial_cleanup_rename (struct diff_queue_struct * pairs )
1872
+ static void initial_cleanup_rename (struct diff_queue_struct * pairs ,
1873
+ struct hashmap * dir_renames )
1672
1874
{
1875
+ struct hashmap_iter iter ;
1876
+ struct dir_rename_entry * e ;
1877
+
1878
+ hashmap_iter_init (dir_renames , & iter );
1879
+ while ((e = hashmap_iter_next (& iter ))) {
1880
+ free (e -> dir );
1881
+ strbuf_release (& e -> new_dir );
1882
+ /* possible_new_dirs already cleared in get_directory_renames */
1883
+ }
1884
+ hashmap_free (dir_renames , 1 );
1885
+ free (dir_renames );
1886
+
1673
1887
free (pairs -> queue );
1674
1888
free (pairs );
1675
1889
}
@@ -1682,6 +1896,7 @@ static int handle_renames(struct merge_options *o,
1682
1896
struct rename_info * ri )
1683
1897
{
1684
1898
struct diff_queue_struct * head_pairs , * merge_pairs ;
1899
+ struct hashmap * dir_re_head , * dir_re_merge ;
1685
1900
int clean ;
1686
1901
1687
1902
ri -> head_renames = NULL ;
@@ -1693,6 +1908,9 @@ static int handle_renames(struct merge_options *o,
1693
1908
head_pairs = get_diffpairs (o , common , head );
1694
1909
merge_pairs = get_diffpairs (o , common , merge );
1695
1910
1911
+ dir_re_head = get_directory_renames (head_pairs , head );
1912
+ dir_re_merge = get_directory_renames (merge_pairs , merge );
1913
+
1696
1914
ri -> head_renames = get_renames (o , head_pairs , head ,
1697
1915
common , head , merge , entries );
1698
1916
ri -> merge_renames = get_renames (o , merge_pairs , merge ,
@@ -1704,8 +1922,8 @@ static int handle_renames(struct merge_options *o,
1704
1922
* data structures are still needed and referenced in
1705
1923
* process_entry(). But there are a few things we can free now.
1706
1924
*/
1707
- initial_cleanup_rename (head_pairs );
1708
- initial_cleanup_rename (merge_pairs );
1925
+ initial_cleanup_rename (head_pairs , dir_re_head );
1926
+ initial_cleanup_rename (merge_pairs , dir_re_merge );
1709
1927
1710
1928
return clean ;
1711
1929
}
0 commit comments