@@ -1510,6 +1510,91 @@ static void remove_hashmap_entries(struct hashmap *dir_renames,
1510
1510
string_list_clear (items_to_remove , 0 );
1511
1511
}
1512
1512
1513
+ /*
1514
+ * See if there is a directory rename for path, and if there are any file
1515
+ * level conflicts for the renamed location. If there is a rename and
1516
+ * there are no conflicts, return the new name. Otherwise, return NULL.
1517
+ */
1518
+ static char * handle_path_level_conflicts (struct merge_options * o ,
1519
+ const char * path ,
1520
+ struct dir_rename_entry * entry ,
1521
+ struct hashmap * collisions ,
1522
+ struct tree * tree )
1523
+ {
1524
+ char * new_path = NULL ;
1525
+ struct collision_entry * collision_ent ;
1526
+ int clean = 1 ;
1527
+ struct strbuf collision_paths = STRBUF_INIT ;
1528
+
1529
+ /*
1530
+ * entry has the mapping of old directory name to new directory name
1531
+ * that we want to apply to path.
1532
+ */
1533
+ new_path = apply_dir_rename (entry , path );
1534
+
1535
+ if (!new_path ) {
1536
+ /* This should only happen when entry->non_unique_new_dir set */
1537
+ if (!entry -> non_unique_new_dir )
1538
+ BUG ("entry->non_unqiue_dir not set and !new_path" );
1539
+ output (o , 1 , _ ("CONFLICT (directory rename split): "
1540
+ "Unclear where to place %s because directory "
1541
+ "%s was renamed to multiple other directories, "
1542
+ "with no destination getting a majority of the "
1543
+ "files." ),
1544
+ path , entry -> dir );
1545
+ clean = 0 ;
1546
+ return NULL ;
1547
+ }
1548
+
1549
+ /*
1550
+ * The caller needs to have ensured that it has pre-populated
1551
+ * collisions with all paths that map to new_path. Do a quick check
1552
+ * to ensure that's the case.
1553
+ */
1554
+ collision_ent = collision_find_entry (collisions , new_path );
1555
+ if (collision_ent == NULL )
1556
+ BUG ("collision_ent is NULL" );
1557
+
1558
+ /*
1559
+ * Check for one-sided add/add/.../add conflicts, i.e.
1560
+ * where implicit renames from the other side doing
1561
+ * directory rename(s) can affect this side of history
1562
+ * to put multiple paths into the same location. Warn
1563
+ * and bail on directory renames for such paths.
1564
+ */
1565
+ if (collision_ent -> reported_already ) {
1566
+ clean = 0 ;
1567
+ } else if (tree_has_path (tree , new_path )) {
1568
+ collision_ent -> reported_already = 1 ;
1569
+ strbuf_add_separated_string_list (& collision_paths , ", " ,
1570
+ & collision_ent -> source_files );
1571
+ output (o , 1 , _ ("CONFLICT (implicit dir rename): Existing "
1572
+ "file/dir at %s in the way of implicit "
1573
+ "directory rename(s) putting the following "
1574
+ "path(s) there: %s." ),
1575
+ new_path , collision_paths .buf );
1576
+ clean = 0 ;
1577
+ } else if (collision_ent -> source_files .nr > 1 ) {
1578
+ collision_ent -> reported_already = 1 ;
1579
+ strbuf_add_separated_string_list (& collision_paths , ", " ,
1580
+ & collision_ent -> source_files );
1581
+ output (o , 1 , _ ("CONFLICT (implicit dir rename): Cannot map "
1582
+ "more than one path to %s; implicit directory "
1583
+ "renames tried to put these paths there: %s" ),
1584
+ new_path , collision_paths .buf );
1585
+ clean = 0 ;
1586
+ }
1587
+
1588
+ /* Free memory we no longer need */
1589
+ strbuf_release (& collision_paths );
1590
+ if (!clean && new_path ) {
1591
+ free (new_path );
1592
+ return NULL ;
1593
+ }
1594
+
1595
+ return new_path ;
1596
+ }
1597
+
1513
1598
/*
1514
1599
* There are a couple things we want to do at the directory level:
1515
1600
* 1. Check for both sides renaming to the same thing, in order to avoid
@@ -1789,6 +1874,59 @@ static void compute_collisions(struct hashmap *collisions,
1789
1874
}
1790
1875
}
1791
1876
1877
+ static char * check_for_directory_rename (struct merge_options * o ,
1878
+ const char * path ,
1879
+ struct tree * tree ,
1880
+ struct hashmap * dir_renames ,
1881
+ struct hashmap * dir_rename_exclusions ,
1882
+ struct hashmap * collisions ,
1883
+ int * clean_merge )
1884
+ {
1885
+ char * new_path = NULL ;
1886
+ struct dir_rename_entry * entry = check_dir_renamed (path , dir_renames );
1887
+ struct dir_rename_entry * oentry = NULL ;
1888
+
1889
+ if (!entry )
1890
+ return new_path ;
1891
+
1892
+ /*
1893
+ * This next part is a little weird. We do not want to do an
1894
+ * implicit rename into a directory we renamed on our side, because
1895
+ * that will result in a spurious rename/rename(1to2) conflict. An
1896
+ * example:
1897
+ * Base commit: dumbdir/afile, otherdir/bfile
1898
+ * Side 1: smrtdir/afile, otherdir/bfile
1899
+ * Side 2: dumbdir/afile, dumbdir/bfile
1900
+ * Here, while working on Side 1, we could notice that otherdir was
1901
+ * renamed/merged to dumbdir, and change the diff_filepair for
1902
+ * otherdir/bfile into a rename into dumbdir/bfile. However, Side
1903
+ * 2 will notice the rename from dumbdir to smrtdir, and do the
1904
+ * transitive rename to move it from dumbdir/bfile to
1905
+ * smrtdir/bfile. That gives us bfile in dumbdir vs being in
1906
+ * smrtdir, a rename/rename(1to2) conflict. We really just want
1907
+ * the file to end up in smrtdir. And the way to achieve that is
1908
+ * to not let Side1 do the rename to dumbdir, since we know that is
1909
+ * the source of one of our directory renames.
1910
+ *
1911
+ * That's why oentry and dir_rename_exclusions is here.
1912
+ *
1913
+ * As it turns out, this also prevents N-way transient rename
1914
+ * confusion; See testcases 9c and 9d of t6043.
1915
+ */
1916
+ oentry = dir_rename_find_entry (dir_rename_exclusions , entry -> new_dir .buf );
1917
+ if (oentry ) {
1918
+ output (o , 1 , _ ("WARNING: Avoiding applying %s -> %s rename "
1919
+ "to %s, because %s itself was renamed." ),
1920
+ entry -> dir , entry -> new_dir .buf , path , entry -> new_dir .buf );
1921
+ } else {
1922
+ new_path = handle_path_level_conflicts (o , path , entry ,
1923
+ collisions , tree );
1924
+ * clean_merge &= (new_path != NULL );
1925
+ }
1926
+
1927
+ return new_path ;
1928
+ }
1929
+
1792
1930
/*
1793
1931
* Get information of all renames which occurred in 'pairs', making use of
1794
1932
* any implicit directory renames inferred from the other side of history.
@@ -1799,11 +1937,13 @@ static void compute_collisions(struct hashmap *collisions,
1799
1937
static struct string_list * get_renames (struct merge_options * o ,
1800
1938
struct diff_queue_struct * pairs ,
1801
1939
struct hashmap * dir_renames ,
1940
+ struct hashmap * dir_rename_exclusions ,
1802
1941
struct tree * tree ,
1803
1942
struct tree * o_tree ,
1804
1943
struct tree * a_tree ,
1805
1944
struct tree * b_tree ,
1806
- struct string_list * entries )
1945
+ struct string_list * entries ,
1946
+ int * clean_merge )
1807
1947
{
1808
1948
int i ;
1809
1949
struct hashmap collisions ;
@@ -1818,11 +1958,22 @@ static struct string_list *get_renames(struct merge_options *o,
1818
1958
struct string_list_item * item ;
1819
1959
struct rename * re ;
1820
1960
struct diff_filepair * pair = pairs -> queue [i ];
1961
+ char * new_path ; /* non-NULL only with directory renames */
1821
1962
1822
- if (pair -> status != 'R' ) {
1963
+ if (pair -> status == 'D' ) {
1964
+ diff_free_filepair (pair );
1965
+ continue ;
1966
+ }
1967
+ new_path = check_for_directory_rename (o , pair -> two -> path , tree ,
1968
+ dir_renames ,
1969
+ dir_rename_exclusions ,
1970
+ & collisions ,
1971
+ clean_merge );
1972
+ if (pair -> status != 'R' && !new_path ) {
1823
1973
diff_free_filepair (pair );
1824
1974
continue ;
1825
1975
}
1976
+
1826
1977
re = xmalloc (sizeof (* re ));
1827
1978
re -> processed = 0 ;
1828
1979
re -> pair = pair ;
@@ -2140,7 +2291,7 @@ static int handle_renames(struct merge_options *o,
2140
2291
{
2141
2292
struct diff_queue_struct * head_pairs , * merge_pairs ;
2142
2293
struct hashmap * dir_re_head , * dir_re_merge ;
2143
- int clean ;
2294
+ int clean = 1 ;
2144
2295
2145
2296
ri -> head_renames = NULL ;
2146
2297
ri -> merge_renames = NULL ;
@@ -2159,13 +2310,20 @@ static int handle_renames(struct merge_options *o,
2159
2310
dir_re_merge , merge );
2160
2311
2161
2312
ri -> head_renames = get_renames (o , head_pairs ,
2162
- dir_re_merge , head ,
2163
- common , head , merge , entries );
2313
+ dir_re_merge , dir_re_head , head ,
2314
+ common , head , merge , entries ,
2315
+ & clean );
2316
+ if (clean < 0 )
2317
+ goto cleanup ;
2164
2318
ri -> merge_renames = get_renames (o , merge_pairs ,
2165
- dir_re_head , merge ,
2166
- common , head , merge , entries );
2167
- clean = process_renames (o , ri -> head_renames , ri -> merge_renames );
2319
+ dir_re_head , dir_re_merge , merge ,
2320
+ common , head , merge , entries ,
2321
+ & clean );
2322
+ if (clean < 0 )
2323
+ goto cleanup ;
2324
+ clean &= process_renames (o , ri -> head_renames , ri -> merge_renames );
2168
2325
2326
+ cleanup :
2169
2327
/*
2170
2328
* Some cleanup is deferred until cleanup_renames() because the
2171
2329
* data structures are still needed and referenced in
0 commit comments