@@ -316,9 +316,14 @@ struct merge_options_internal {
316316 * (e.g. "drivers/firmware/raspberrypi.c").
317317 * * store all relevant paths in the repo, both directories and
318318 * 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.
322327 *
323328 * The values of paths:
324329 * * 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,
21632168 /*
21642169 * FIXME: If opt->priv->call_depth && !clean, then we really
21652170 * 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
21672172 * regular file" to fail. It would be best to use some other
21682173 * mode, but we'll confuse all kinds of stuff if we use one
21692174 * 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,
23132318 return strbuf_detach (& new_path , NULL );
23142319}
23152320
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 )
23172325{
23182326 struct merged_info * mi = strmap_get (paths , path );
23192327 struct conflict_info * ci ;
23202328 if (!mi )
23212329 return 0 ;
23222330 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 ));
23242335}
23252336
23262337/*
@@ -2332,6 +2343,7 @@ static int path_in_way(struct strmap *paths, const char *path, unsigned side_mas
23322343static char * handle_path_level_conflicts (struct merge_options * opt ,
23332344 const char * path ,
23342345 unsigned side_index ,
2346+ struct diff_filepair * p ,
23352347 struct strmap_entry * rename_info ,
23362348 struct strmap * collisions )
23372349{
@@ -2366,7 +2378,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
23662378 */
23672379 if (c_info -> reported_already ) {
23682380 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 )) {
23702382 c_info -> reported_already = 1 ;
23712383 strbuf_add_separated_string_list (& collision_paths , ", " ,
23722384 & c_info -> source_files );
@@ -2520,7 +2532,7 @@ static void compute_collisions(struct strmap *collisions,
25202532 * happening, and fall back to no-directory-rename detection
25212533 * behavior for those paths.
25222534 *
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.
25242536 */
25252537 for (i = 0 ; i < pairs -> nr ; ++ i ) {
25262538 struct strmap_entry * rename_info ;
@@ -2573,14 +2585,14 @@ static void free_collisions(struct strmap *collisions)
25732585static char * check_for_directory_rename (struct merge_options * opt ,
25742586 const char * path ,
25752587 unsigned side_index ,
2588+ struct diff_filepair * p ,
25762589 struct strmap * dir_renames ,
25772590 struct strmap * dir_rename_exclusions ,
25782591 struct strmap * collisions ,
25792592 int * clean_merge )
25802593{
25812594 char * new_path ;
25822595 struct strmap_entry * rename_info ;
2583- struct strmap_entry * otherinfo ;
25842596 const char * new_dir ;
25852597 int other_side = 3 - side_index ;
25862598
@@ -2615,14 +2627,13 @@ static char *check_for_directory_rename(struct merge_options *opt,
26152627 * to not let Side1 do the rename to dumbdir, since we know that is
26162628 * the source of one of our directory renames.
26172629 *
2618- * That's why otherinfo and dir_rename_exclusions is here.
2630+ * That's why dir_rename_exclusions is here.
26192631 *
26202632 * 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 .
26222634 */
26232635 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 )) {
26262637 path_msg (opt , INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME , 1 ,
26272638 rename_info -> key , path , new_dir , NULL ,
26282639 _ ("WARNING: Avoiding applying %s -> %s rename "
@@ -2631,7 +2642,7 @@ static char *check_for_directory_rename(struct merge_options *opt,
26312642 return NULL ;
26322643 }
26332644
2634- new_path = handle_path_level_conflicts (opt , path , side_index ,
2645+ new_path = handle_path_level_conflicts (opt , path , side_index , p ,
26352646 rename_info ,
26362647 & collisions [side_index ]);
26372648 * clean_merge &= (new_path != NULL );
@@ -2875,6 +2886,20 @@ static int process_renames(struct merge_options *opt,
28752886 newinfo = new_ent -> value ;
28762887 }
28772888
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+
28782903 /*
28792904 * If pair->one->path isn't in opt->priv->paths, that means
28802905 * that either directory rename detection removed that
@@ -3419,7 +3444,7 @@ static int collect_renames(struct merge_options *opt,
34193444 }
34203445
34213446 new_path = check_for_directory_rename (opt , p -> two -> path ,
3422- side_index ,
3447+ side_index , p ,
34233448 dir_renames_for_side ,
34243449 rename_exclusions ,
34253450 collisions ,
0 commit comments