@@ -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 ) {
@@ -1347,6 +1385,169 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
1347
1385
return ret ;
1348
1386
}
1349
1387
1388
+ static void get_renamed_dir_portion (const char * old_path , const char * new_path ,
1389
+ char * * old_dir , char * * new_dir )
1390
+ {
1391
+ char * end_of_old , * end_of_new ;
1392
+ int old_len , new_len ;
1393
+
1394
+ * old_dir = NULL ;
1395
+ * new_dir = NULL ;
1396
+
1397
+ /*
1398
+ * For
1399
+ * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
1400
+ * the "e/foo.c" part is the same, we just want to know that
1401
+ * "a/b/c/d" was renamed to "a/b/some/thing/else"
1402
+ * so, for this example, this function returns "a/b/c/d" in
1403
+ * *old_dir and "a/b/some/thing/else" in *new_dir.
1404
+ *
1405
+ * Also, if the basename of the file changed, we don't care. We
1406
+ * want to know which portion of the directory, if any, changed.
1407
+ */
1408
+ end_of_old = strrchr (old_path , '/' );
1409
+ end_of_new = strrchr (new_path , '/' );
1410
+
1411
+ if (end_of_old == NULL || end_of_new == NULL )
1412
+ return ;
1413
+ while (* -- end_of_new == * -- end_of_old &&
1414
+ end_of_old != old_path &&
1415
+ end_of_new != new_path )
1416
+ ; /* Do nothing; all in the while loop */
1417
+ /*
1418
+ * We've found the first non-matching character in the directory
1419
+ * paths. That means the current directory we were comparing
1420
+ * represents the rename. Move end_of_old and end_of_new back
1421
+ * to the full directory name.
1422
+ */
1423
+ if (* end_of_old == '/' )
1424
+ end_of_old ++ ;
1425
+ if (* end_of_old != '/' )
1426
+ end_of_new ++ ;
1427
+ end_of_old = strchr (end_of_old , '/' );
1428
+ end_of_new = strchr (end_of_new , '/' );
1429
+
1430
+ /*
1431
+ * It may have been the case that old_path and new_path were the same
1432
+ * directory all along. Don't claim a rename if they're the same.
1433
+ */
1434
+ old_len = end_of_old - old_path ;
1435
+ new_len = end_of_new - new_path ;
1436
+
1437
+ if (old_len != new_len || strncmp (old_path , new_path , old_len )) {
1438
+ * old_dir = xstrndup (old_path , old_len );
1439
+ * new_dir = xstrndup (new_path , new_len );
1440
+ }
1441
+ }
1442
+
1443
+ static struct hashmap * get_directory_renames (struct diff_queue_struct * pairs ,
1444
+ struct tree * tree )
1445
+ {
1446
+ struct hashmap * dir_renames ;
1447
+ struct hashmap_iter iter ;
1448
+ struct dir_rename_entry * entry ;
1449
+ int i ;
1450
+
1451
+ /*
1452
+ * Typically, we think of a directory rename as all files from a
1453
+ * certain directory being moved to a target directory. However,
1454
+ * what if someone first moved two files from the original
1455
+ * directory in one commit, and then renamed the directory
1456
+ * somewhere else in a later commit? At merge time, we just know
1457
+ * that files from the original directory went to two different
1458
+ * places, and that the bulk of them ended up in the same place.
1459
+ * We want each directory rename to represent where the bulk of the
1460
+ * files from that directory end up; this function exists to find
1461
+ * where the bulk of the files went.
1462
+ *
1463
+ * The first loop below simply iterates through the list of file
1464
+ * renames, finding out how often each directory rename pair
1465
+ * possibility occurs.
1466
+ */
1467
+ dir_renames = xmalloc (sizeof (struct hashmap ));
1468
+ dir_rename_init (dir_renames );
1469
+ for (i = 0 ; i < pairs -> nr ; ++ i ) {
1470
+ struct string_list_item * item ;
1471
+ int * count ;
1472
+ struct diff_filepair * pair = pairs -> queue [i ];
1473
+ char * old_dir , * new_dir ;
1474
+
1475
+ /* File not part of directory rename if it wasn't renamed */
1476
+ if (pair -> status != 'R' )
1477
+ continue ;
1478
+
1479
+ get_renamed_dir_portion (pair -> one -> path , pair -> two -> path ,
1480
+ & old_dir , & new_dir );
1481
+ if (!old_dir )
1482
+ /* Directory didn't change at all; ignore this one. */
1483
+ continue ;
1484
+
1485
+ entry = dir_rename_find_entry (dir_renames , old_dir );
1486
+ if (!entry ) {
1487
+ entry = xmalloc (sizeof (struct dir_rename_entry ));
1488
+ dir_rename_entry_init (entry , old_dir );
1489
+ hashmap_put (dir_renames , entry );
1490
+ } else {
1491
+ free (old_dir );
1492
+ }
1493
+ item = string_list_lookup (& entry -> possible_new_dirs , new_dir );
1494
+ if (!item ) {
1495
+ item = string_list_insert (& entry -> possible_new_dirs ,
1496
+ new_dir );
1497
+ item -> util = xcalloc (1 , sizeof (int ));
1498
+ } else {
1499
+ free (new_dir );
1500
+ }
1501
+ count = item -> util ;
1502
+ * count += 1 ;
1503
+ }
1504
+
1505
+ /*
1506
+ * For each directory with files moved out of it, we find out which
1507
+ * target directory received the most files so we can declare it to
1508
+ * be the "winning" target location for the directory rename. This
1509
+ * winner gets recorded in new_dir. If there is no winner
1510
+ * (multiple target directories received the same number of files),
1511
+ * we set non_unique_new_dir. Once we've determined the winner (or
1512
+ * that there is no winner), we no longer need possible_new_dirs.
1513
+ */
1514
+ hashmap_iter_init (dir_renames , & iter );
1515
+ while ((entry = hashmap_iter_next (& iter ))) {
1516
+ int max = 0 ;
1517
+ int bad_max = 0 ;
1518
+ char * best = NULL ;
1519
+
1520
+ for (i = 0 ; i < entry -> possible_new_dirs .nr ; i ++ ) {
1521
+ int * count = entry -> possible_new_dirs .items [i ].util ;
1522
+
1523
+ if (* count == max )
1524
+ bad_max = max ;
1525
+ else if (* count > max ) {
1526
+ max = * count ;
1527
+ best = entry -> possible_new_dirs .items [i ].string ;
1528
+ }
1529
+ }
1530
+ if (bad_max == max )
1531
+ entry -> non_unique_new_dir = 1 ;
1532
+ else {
1533
+ assert (entry -> new_dir .len == 0 );
1534
+ strbuf_addstr (& entry -> new_dir , best );
1535
+ }
1536
+ /*
1537
+ * The relevant directory sub-portion of the original full
1538
+ * filepaths were xstrndup'ed before inserting into
1539
+ * possible_new_dirs, and instead of manually iterating the
1540
+ * list and free'ing each, just lie and tell
1541
+ * possible_new_dirs that it did the strdup'ing so that it
1542
+ * will free them for us.
1543
+ */
1544
+ entry -> possible_new_dirs .strdup_strings = 1 ;
1545
+ string_list_clear (& entry -> possible_new_dirs , 1 );
1546
+ }
1547
+
1548
+ return dir_renames ;
1549
+ }
1550
+
1350
1551
/*
1351
1552
* Get information of all renames which occurred in 'pairs', making use of
1352
1553
* any implicit directory renames inferred from the other side of history.
@@ -1658,8 +1859,21 @@ struct rename_info {
1658
1859
struct string_list * merge_renames ;
1659
1860
};
1660
1861
1661
- static void initial_cleanup_rename (struct diff_queue_struct * pairs )
1862
+ static void initial_cleanup_rename (struct diff_queue_struct * pairs ,
1863
+ struct hashmap * dir_renames )
1662
1864
{
1865
+ struct hashmap_iter iter ;
1866
+ struct dir_rename_entry * e ;
1867
+
1868
+ hashmap_iter_init (dir_renames , & iter );
1869
+ while ((e = hashmap_iter_next (& iter ))) {
1870
+ free (e -> dir );
1871
+ strbuf_release (& e -> new_dir );
1872
+ /* possible_new_dirs already cleared in get_directory_renames */
1873
+ }
1874
+ hashmap_free (dir_renames , 1 );
1875
+ free (dir_renames );
1876
+
1663
1877
free (pairs -> queue );
1664
1878
free (pairs );
1665
1879
}
@@ -1672,6 +1886,7 @@ static int handle_renames(struct merge_options *o,
1672
1886
struct rename_info * ri )
1673
1887
{
1674
1888
struct diff_queue_struct * head_pairs , * merge_pairs ;
1889
+ struct hashmap * dir_re_head , * dir_re_merge ;
1675
1890
int clean ;
1676
1891
1677
1892
ri -> head_renames = NULL ;
@@ -1683,6 +1898,9 @@ static int handle_renames(struct merge_options *o,
1683
1898
head_pairs = get_diffpairs (o , common , head );
1684
1899
merge_pairs = get_diffpairs (o , common , merge );
1685
1900
1901
+ dir_re_head = get_directory_renames (head_pairs , head );
1902
+ dir_re_merge = get_directory_renames (merge_pairs , merge );
1903
+
1686
1904
ri -> head_renames = get_renames (o , head_pairs , head ,
1687
1905
common , head , merge , entries );
1688
1906
ri -> merge_renames = get_renames (o , merge_pairs , merge ,
@@ -1694,8 +1912,8 @@ static int handle_renames(struct merge_options *o,
1694
1912
* data structures are still needed and referenced in
1695
1913
* process_entry(). But there are a few things we can free now.
1696
1914
*/
1697
- initial_cleanup_rename (head_pairs );
1698
- initial_cleanup_rename (merge_pairs );
1915
+ initial_cleanup_rename (head_pairs , dir_re_head );
1916
+ initial_cleanup_rename (merge_pairs , dir_re_merge );
1699
1917
1700
1918
return clean ;
1701
1919
}
0 commit comments