@@ -87,6 +87,29 @@ static void dir_rename_entry_init(struct dir_rename_entry *entry,
87
87
string_list_init (& entry -> possible_new_dirs , 0 );
88
88
}
89
89
90
+ static struct collision_entry * collision_find_entry (struct hashmap * hashmap ,
91
+ char * target_file )
92
+ {
93
+ struct collision_entry key ;
94
+
95
+ hashmap_entry_init (& key , strhash (target_file ));
96
+ key .target_file = target_file ;
97
+ return hashmap_get (hashmap , & key , NULL );
98
+ }
99
+
100
+ static int collision_cmp (void * unused_cmp_data ,
101
+ const struct collision_entry * e1 ,
102
+ const struct collision_entry * e2 ,
103
+ const void * unused_keydata )
104
+ {
105
+ return strcmp (e1 -> target_file , e2 -> target_file );
106
+ }
107
+
108
+ static void collision_init (struct hashmap * map )
109
+ {
110
+ hashmap_init (map , (hashmap_cmp_fn ) collision_cmp , NULL , 0 );
111
+ }
112
+
90
113
static void flush_output (struct merge_options * o )
91
114
{
92
115
if (o -> buffer_output < 2 && o -> obuf .len ) {
@@ -1394,6 +1417,31 @@ static int tree_has_path(struct tree *tree, const char *path)
1394
1417
hashy , & mode_o );
1395
1418
}
1396
1419
1420
+ /*
1421
+ * Return a new string that replaces the beginning portion (which matches
1422
+ * entry->dir), with entry->new_dir. In perl-speak:
1423
+ * new_path_name = (old_path =~ s/entry->dir/entry->new_dir/);
1424
+ * NOTE:
1425
+ * Caller must ensure that old_path starts with entry->dir + '/'.
1426
+ */
1427
+ static char * apply_dir_rename (struct dir_rename_entry * entry ,
1428
+ const char * old_path )
1429
+ {
1430
+ struct strbuf new_path = STRBUF_INIT ;
1431
+ int oldlen , newlen ;
1432
+
1433
+ if (entry -> non_unique_new_dir )
1434
+ return NULL ;
1435
+
1436
+ oldlen = strlen (entry -> dir );
1437
+ newlen = entry -> new_dir .len + (strlen (old_path ) - oldlen ) + 1 ;
1438
+ strbuf_grow (& new_path , newlen );
1439
+ strbuf_addbuf (& new_path , & entry -> new_dir );
1440
+ strbuf_addstr (& new_path , & old_path [oldlen ]);
1441
+
1442
+ return strbuf_detach (& new_path , NULL );
1443
+ }
1444
+
1397
1445
static void get_renamed_dir_portion (const char * old_path , const char * new_path ,
1398
1446
char * * old_dir , char * * new_dir )
1399
1447
{
@@ -1663,6 +1711,84 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
1663
1711
return dir_renames ;
1664
1712
}
1665
1713
1714
+ static struct dir_rename_entry * check_dir_renamed (const char * path ,
1715
+ struct hashmap * dir_renames )
1716
+ {
1717
+ char temp [PATH_MAX ];
1718
+ char * end ;
1719
+ struct dir_rename_entry * entry ;
1720
+
1721
+ strcpy (temp , path );
1722
+ while ((end = strrchr (temp , '/' ))) {
1723
+ * end = '\0' ;
1724
+ entry = dir_rename_find_entry (dir_renames , temp );
1725
+ if (entry )
1726
+ return entry ;
1727
+ }
1728
+ return NULL ;
1729
+ }
1730
+
1731
+ static void compute_collisions (struct hashmap * collisions ,
1732
+ struct hashmap * dir_renames ,
1733
+ struct diff_queue_struct * pairs )
1734
+ {
1735
+ int i ;
1736
+
1737
+ /*
1738
+ * Multiple files can be mapped to the same path due to directory
1739
+ * renames done by the other side of history. Since that other
1740
+ * side of history could have merged multiple directories into one,
1741
+ * if our side of history added the same file basename to each of
1742
+ * those directories, then all N of them would get implicitly
1743
+ * renamed by the directory rename detection into the same path,
1744
+ * and we'd get an add/add/.../add conflict, and all those adds
1745
+ * from *this* side of history. This is not representable in the
1746
+ * index, and users aren't going to easily be able to make sense of
1747
+ * it. So we need to provide a good warning about what's
1748
+ * happening, and fall back to no-directory-rename detection
1749
+ * behavior for those paths.
1750
+ *
1751
+ * See testcases 9e and all of section 5 from t6043 for examples.
1752
+ */
1753
+ collision_init (collisions );
1754
+
1755
+ for (i = 0 ; i < pairs -> nr ; ++ i ) {
1756
+ struct dir_rename_entry * dir_rename_ent ;
1757
+ struct collision_entry * collision_ent ;
1758
+ char * new_path ;
1759
+ struct diff_filepair * pair = pairs -> queue [i ];
1760
+
1761
+ if (pair -> status == 'D' )
1762
+ continue ;
1763
+ dir_rename_ent = check_dir_renamed (pair -> two -> path ,
1764
+ dir_renames );
1765
+ if (!dir_rename_ent )
1766
+ continue ;
1767
+
1768
+ new_path = apply_dir_rename (dir_rename_ent , pair -> two -> path );
1769
+ if (!new_path )
1770
+ /*
1771
+ * dir_rename_ent->non_unique_new_path is true, which
1772
+ * means there is no directory rename for us to use,
1773
+ * which means it won't cause us any additional
1774
+ * collisions.
1775
+ */
1776
+ continue ;
1777
+ collision_ent = collision_find_entry (collisions , new_path );
1778
+ if (!collision_ent ) {
1779
+ collision_ent = xcalloc (1 ,
1780
+ sizeof (struct collision_entry ));
1781
+ hashmap_entry_init (collision_ent , strhash (new_path ));
1782
+ hashmap_put (collisions , collision_ent );
1783
+ collision_ent -> target_file = new_path ;
1784
+ } else {
1785
+ free (new_path );
1786
+ }
1787
+ string_list_insert (& collision_ent -> source_files ,
1788
+ pair -> two -> path );
1789
+ }
1790
+ }
1791
+
1666
1792
/*
1667
1793
* Get information of all renames which occurred in 'pairs', making use of
1668
1794
* any implicit directory renames inferred from the other side of history.
@@ -1672,15 +1798,20 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
1672
1798
*/
1673
1799
static struct string_list * get_renames (struct merge_options * o ,
1674
1800
struct diff_queue_struct * pairs ,
1801
+ struct hashmap * dir_renames ,
1675
1802
struct tree * tree ,
1676
1803
struct tree * o_tree ,
1677
1804
struct tree * a_tree ,
1678
1805
struct tree * b_tree ,
1679
1806
struct string_list * entries )
1680
1807
{
1681
1808
int i ;
1809
+ struct hashmap collisions ;
1810
+ struct hashmap_iter iter ;
1811
+ struct collision_entry * e ;
1682
1812
struct string_list * renames ;
1683
1813
1814
+ compute_collisions (& collisions , dir_renames , pairs );
1684
1815
renames = xcalloc (1 , sizeof (struct string_list ));
1685
1816
1686
1817
for (i = 0 ; i < pairs -> nr ; ++ i ) {
@@ -1711,6 +1842,13 @@ static struct string_list *get_renames(struct merge_options *o,
1711
1842
item = string_list_insert (renames , pair -> one -> path );
1712
1843
item -> util = re ;
1713
1844
}
1845
+
1846
+ hashmap_iter_init (& collisions , & iter );
1847
+ while ((e = hashmap_iter_next (& iter ))) {
1848
+ free (e -> target_file );
1849
+ string_list_clear (& e -> source_files , 0 );
1850
+ }
1851
+ hashmap_free (& collisions , 1 );
1714
1852
return renames ;
1715
1853
}
1716
1854
@@ -2020,9 +2158,11 @@ static int handle_renames(struct merge_options *o,
2020
2158
dir_re_head , head ,
2021
2159
dir_re_merge , merge );
2022
2160
2023
- ri -> head_renames = get_renames (o , head_pairs , head ,
2024
- common , head , merge , entries );
2025
- ri -> merge_renames = get_renames (o , merge_pairs , merge ,
2161
+ ri -> head_renames = get_renames (o , head_pairs ,
2162
+ dir_re_merge , head ,
2163
+ common , head , merge , entries );
2164
+ ri -> merge_renames = get_renames (o , merge_pairs ,
2165
+ dir_re_head , merge ,
2026
2166
common , head , merge , entries );
2027
2167
clean = process_renames (o , ri -> head_renames , ri -> merge_renames );
2028
2168
0 commit comments