@@ -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 ) {
@@ -1404,6 +1427,31 @@ static int tree_has_path(struct tree *tree, const char *path)
1404
1427
& hashy , & mode_o );
1405
1428
}
1406
1429
1430
+ /*
1431
+ * Return a new string that replaces the beginning portion (which matches
1432
+ * entry->dir), with entry->new_dir. In perl-speak:
1433
+ * new_path_name = (old_path =~ s/entry->dir/entry->new_dir/);
1434
+ * NOTE:
1435
+ * Caller must ensure that old_path starts with entry->dir + '/'.
1436
+ */
1437
+ static char * apply_dir_rename (struct dir_rename_entry * entry ,
1438
+ const char * old_path )
1439
+ {
1440
+ struct strbuf new_path = STRBUF_INIT ;
1441
+ int oldlen , newlen ;
1442
+
1443
+ if (entry -> non_unique_new_dir )
1444
+ return NULL ;
1445
+
1446
+ oldlen = strlen (entry -> dir );
1447
+ newlen = entry -> new_dir .len + (strlen (old_path ) - oldlen ) + 1 ;
1448
+ strbuf_grow (& new_path , newlen );
1449
+ strbuf_addbuf (& new_path , & entry -> new_dir );
1450
+ strbuf_addstr (& new_path , & old_path [oldlen ]);
1451
+
1452
+ return strbuf_detach (& new_path , NULL );
1453
+ }
1454
+
1407
1455
static void get_renamed_dir_portion (const char * old_path , const char * new_path ,
1408
1456
char * * old_dir , char * * new_dir )
1409
1457
{
@@ -1673,6 +1721,84 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
1673
1721
return dir_renames ;
1674
1722
}
1675
1723
1724
+ static struct dir_rename_entry * check_dir_renamed (const char * path ,
1725
+ struct hashmap * dir_renames )
1726
+ {
1727
+ char temp [PATH_MAX ];
1728
+ char * end ;
1729
+ struct dir_rename_entry * entry ;
1730
+
1731
+ strcpy (temp , path );
1732
+ while ((end = strrchr (temp , '/' ))) {
1733
+ * end = '\0' ;
1734
+ entry = dir_rename_find_entry (dir_renames , temp );
1735
+ if (entry )
1736
+ return entry ;
1737
+ }
1738
+ return NULL ;
1739
+ }
1740
+
1741
+ static void compute_collisions (struct hashmap * collisions ,
1742
+ struct hashmap * dir_renames ,
1743
+ struct diff_queue_struct * pairs )
1744
+ {
1745
+ int i ;
1746
+
1747
+ /*
1748
+ * Multiple files can be mapped to the same path due to directory
1749
+ * renames done by the other side of history. Since that other
1750
+ * side of history could have merged multiple directories into one,
1751
+ * if our side of history added the same file basename to each of
1752
+ * those directories, then all N of them would get implicitly
1753
+ * renamed by the directory rename detection into the same path,
1754
+ * and we'd get an add/add/.../add conflict, and all those adds
1755
+ * from *this* side of history. This is not representable in the
1756
+ * index, and users aren't going to easily be able to make sense of
1757
+ * it. So we need to provide a good warning about what's
1758
+ * happening, and fall back to no-directory-rename detection
1759
+ * behavior for those paths.
1760
+ *
1761
+ * See testcases 9e and all of section 5 from t6043 for examples.
1762
+ */
1763
+ collision_init (collisions );
1764
+
1765
+ for (i = 0 ; i < pairs -> nr ; ++ i ) {
1766
+ struct dir_rename_entry * dir_rename_ent ;
1767
+ struct collision_entry * collision_ent ;
1768
+ char * new_path ;
1769
+ struct diff_filepair * pair = pairs -> queue [i ];
1770
+
1771
+ if (pair -> status == 'D' )
1772
+ continue ;
1773
+ dir_rename_ent = check_dir_renamed (pair -> two -> path ,
1774
+ dir_renames );
1775
+ if (!dir_rename_ent )
1776
+ continue ;
1777
+
1778
+ new_path = apply_dir_rename (dir_rename_ent , pair -> two -> path );
1779
+ if (!new_path )
1780
+ /*
1781
+ * dir_rename_ent->non_unique_new_path is true, which
1782
+ * means there is no directory rename for us to use,
1783
+ * which means it won't cause us any additional
1784
+ * collisions.
1785
+ */
1786
+ continue ;
1787
+ collision_ent = collision_find_entry (collisions , new_path );
1788
+ if (!collision_ent ) {
1789
+ collision_ent = xcalloc (1 ,
1790
+ sizeof (struct collision_entry ));
1791
+ hashmap_entry_init (collision_ent , strhash (new_path ));
1792
+ hashmap_put (collisions , collision_ent );
1793
+ collision_ent -> target_file = new_path ;
1794
+ } else {
1795
+ free (new_path );
1796
+ }
1797
+ string_list_insert (& collision_ent -> source_files ,
1798
+ pair -> two -> path );
1799
+ }
1800
+ }
1801
+
1676
1802
/*
1677
1803
* Get information of all renames which occurred in 'pairs', making use of
1678
1804
* any implicit directory renames inferred from the other side of history.
@@ -1682,15 +1808,20 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
1682
1808
*/
1683
1809
static struct string_list * get_renames (struct merge_options * o ,
1684
1810
struct diff_queue_struct * pairs ,
1811
+ struct hashmap * dir_renames ,
1685
1812
struct tree * tree ,
1686
1813
struct tree * o_tree ,
1687
1814
struct tree * a_tree ,
1688
1815
struct tree * b_tree ,
1689
1816
struct string_list * entries )
1690
1817
{
1691
1818
int i ;
1819
+ struct hashmap collisions ;
1820
+ struct hashmap_iter iter ;
1821
+ struct collision_entry * e ;
1692
1822
struct string_list * renames ;
1693
1823
1824
+ compute_collisions (& collisions , dir_renames , pairs );
1694
1825
renames = xcalloc (1 , sizeof (struct string_list ));
1695
1826
1696
1827
for (i = 0 ; i < pairs -> nr ; ++ i ) {
@@ -1721,6 +1852,13 @@ static struct string_list *get_renames(struct merge_options *o,
1721
1852
item = string_list_insert (renames , pair -> one -> path );
1722
1853
item -> util = re ;
1723
1854
}
1855
+
1856
+ hashmap_iter_init (& collisions , & iter );
1857
+ while ((e = hashmap_iter_next (& iter ))) {
1858
+ free (e -> target_file );
1859
+ string_list_clear (& e -> source_files , 0 );
1860
+ }
1861
+ hashmap_free (& collisions , 1 );
1724
1862
return renames ;
1725
1863
}
1726
1864
@@ -2030,9 +2168,11 @@ static int handle_renames(struct merge_options *o,
2030
2168
dir_re_head , head ,
2031
2169
dir_re_merge , merge );
2032
2170
2033
- ri -> head_renames = get_renames (o , head_pairs , head ,
2034
- common , head , merge , entries );
2035
- ri -> merge_renames = get_renames (o , merge_pairs , merge ,
2171
+ ri -> head_renames = get_renames (o , head_pairs ,
2172
+ dir_re_merge , head ,
2173
+ common , head , merge , entries );
2174
+ ri -> merge_renames = get_renames (o , merge_pairs ,
2175
+ dir_re_head , merge ,
2036
2176
common , head , merge , entries );
2037
2177
clean = process_renames (o , ri -> head_renames , ri -> merge_renames );
2038
2178
0 commit comments