@@ -316,9 +316,14 @@ struct merge_options_internal {
316
316
* (e.g. "drivers/firmware/raspberrypi.c").
317
317
* * store all relevant paths in the repo, both directories and
318
318
* files (e.g. drivers, drivers/firmware would also be included)
319
- * * these keys serve to intern all the path strings, which allows
320
- * us to do pointer comparison on directory names instead of
321
- * strcmp; we just have to be careful to use the interned strings.
319
+ * * these keys serve to intern *all* path strings, which allows us
320
+ * to do pointer comparisons on file & directory names instead of
321
+ * using strcmp; however, for this pointer-comparison optimization
322
+ * to work, any code path that independently computes a path needs
323
+ * to check for it existing in this strmap, and if so, point to
324
+ * the path in this strmap instead of their computed copy. See
325
+ * the "reuse known pointer" comment in
326
+ * apply_directory_rename_modifications() for an example.
322
327
*
323
328
* The values of paths:
324
329
* * either a pointer to a merged_info, or a conflict_info struct
@@ -2163,7 +2168,7 @@ static int handle_content_merge(struct merge_options *opt,
2163
2168
/*
2164
2169
* FIXME: If opt->priv->call_depth && !clean, then we really
2165
2170
* should not make result->mode match either a->mode or
2166
- * b->mode; that causes t6036 "check conflicting mode for
2171
+ * b->mode; that causes t6416 "check conflicting mode for
2167
2172
* regular file" to fail. It would be best to use some other
2168
2173
* mode, but we'll confuse all kinds of stuff if we use one
2169
2174
* where S_ISREG(result->mode) isn't true, and if we use
@@ -2313,14 +2318,20 @@ static char *apply_dir_rename(struct strmap_entry *rename_info,
2313
2318
return strbuf_detach (& new_path , NULL );
2314
2319
}
2315
2320
2316
- static int path_in_way (struct strmap * paths , const char * path , unsigned side_mask )
2321
+ static int path_in_way (struct strmap * paths ,
2322
+ const char * path ,
2323
+ unsigned side_mask ,
2324
+ struct diff_filepair * p )
2317
2325
{
2318
2326
struct merged_info * mi = strmap_get (paths , path );
2319
2327
struct conflict_info * ci ;
2320
2328
if (!mi )
2321
2329
return 0 ;
2322
2330
INITIALIZE_CI (ci , mi );
2323
- return mi -> clean || (side_mask & (ci -> filemask | ci -> dirmask ));
2331
+ return mi -> clean || (side_mask & (ci -> filemask | ci -> dirmask ))
2332
+ /* See testcases 12[npq] of t6423 for this next condition */
2333
+ || ((ci -> filemask & 0x01 ) &&
2334
+ strcmp (p -> one -> path , path ));
2324
2335
}
2325
2336
2326
2337
/*
@@ -2332,6 +2343,7 @@ static int path_in_way(struct strmap *paths, const char *path, unsigned side_mas
2332
2343
static char * handle_path_level_conflicts (struct merge_options * opt ,
2333
2344
const char * path ,
2334
2345
unsigned side_index ,
2346
+ struct diff_filepair * p ,
2335
2347
struct strmap_entry * rename_info ,
2336
2348
struct strmap * collisions )
2337
2349
{
@@ -2366,7 +2378,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
2366
2378
*/
2367
2379
if (c_info -> reported_already ) {
2368
2380
clean = 0 ;
2369
- } else if (path_in_way (& opt -> priv -> paths , new_path , 1 << side_index )) {
2381
+ } else if (path_in_way (& opt -> priv -> paths , new_path , 1 << side_index , p )) {
2370
2382
c_info -> reported_already = 1 ;
2371
2383
strbuf_add_separated_string_list (& collision_paths , ", " ,
2372
2384
& c_info -> source_files );
@@ -2520,7 +2532,7 @@ static void compute_collisions(struct strmap *collisions,
2520
2532
* happening, and fall back to no-directory-rename detection
2521
2533
* behavior for those paths.
2522
2534
*
2523
- * See testcases 9e and all of section 5 from t6043 for examples.
2535
+ * See testcases 9e and all of section 5 from t6423 for examples.
2524
2536
*/
2525
2537
for (i = 0 ; i < pairs -> nr ; ++ i ) {
2526
2538
struct strmap_entry * rename_info ;
@@ -2573,14 +2585,14 @@ static void free_collisions(struct strmap *collisions)
2573
2585
static char * check_for_directory_rename (struct merge_options * opt ,
2574
2586
const char * path ,
2575
2587
unsigned side_index ,
2588
+ struct diff_filepair * p ,
2576
2589
struct strmap * dir_renames ,
2577
2590
struct strmap * dir_rename_exclusions ,
2578
2591
struct strmap * collisions ,
2579
2592
int * clean_merge )
2580
2593
{
2581
2594
char * new_path ;
2582
2595
struct strmap_entry * rename_info ;
2583
- struct strmap_entry * otherinfo ;
2584
2596
const char * new_dir ;
2585
2597
int other_side = 3 - side_index ;
2586
2598
@@ -2615,14 +2627,13 @@ static char *check_for_directory_rename(struct merge_options *opt,
2615
2627
* to not let Side1 do the rename to dumbdir, since we know that is
2616
2628
* the source of one of our directory renames.
2617
2629
*
2618
- * That's why otherinfo and dir_rename_exclusions is here.
2630
+ * That's why dir_rename_exclusions is here.
2619
2631
*
2620
2632
* As it turns out, this also prevents N-way transient rename
2621
- * confusion; See testcases 9c and 9d of t6043 .
2633
+ * confusion; See testcases 9c and 9d of t6423 .
2622
2634
*/
2623
2635
new_dir = rename_info -> value ; /* old_dir = rename_info->key; */
2624
- otherinfo = strmap_get_entry (dir_rename_exclusions , new_dir );
2625
- if (otherinfo ) {
2636
+ if (strmap_contains (dir_rename_exclusions , new_dir )) {
2626
2637
path_msg (opt , INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME , 1 ,
2627
2638
rename_info -> key , path , new_dir , NULL ,
2628
2639
_ ("WARNING: Avoiding applying %s -> %s rename "
@@ -2631,7 +2642,7 @@ static char *check_for_directory_rename(struct merge_options *opt,
2631
2642
return NULL ;
2632
2643
}
2633
2644
2634
- new_path = handle_path_level_conflicts (opt , path , side_index ,
2645
+ new_path = handle_path_level_conflicts (opt , path , side_index , p ,
2635
2646
rename_info ,
2636
2647
& collisions [side_index ]);
2637
2648
* clean_merge &= (new_path != NULL );
@@ -2875,6 +2886,20 @@ static int process_renames(struct merge_options *opt,
2875
2886
newinfo = new_ent -> value ;
2876
2887
}
2877
2888
2889
+ /*
2890
+ * Directory renames can result in rename-to-self; the code
2891
+ * below assumes we have A->B with different A & B, and tries
2892
+ * to move all entries to path B. If A & B are the same path,
2893
+ * the logic can get confused, so skip further processing when
2894
+ * A & B are already the same path.
2895
+ *
2896
+ * As a reminder, we can avoid strcmp here because all paths
2897
+ * are interned in opt->priv->paths; see the comment above
2898
+ * "paths" in struct merge_options_internal.
2899
+ */
2900
+ if (oldpath == newpath )
2901
+ continue ;
2902
+
2878
2903
/*
2879
2904
* If pair->one->path isn't in opt->priv->paths, that means
2880
2905
* that either directory rename detection removed that
@@ -3419,7 +3444,7 @@ static int collect_renames(struct merge_options *opt,
3419
3444
}
3420
3445
3421
3446
new_path = check_for_directory_rename (opt , p -> two -> path ,
3422
- side_index ,
3447
+ side_index , p ,
3423
3448
dir_renames_for_side ,
3424
3449
rename_exclusions ,
3425
3450
collisions ,
0 commit comments